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

Support attr readers for fields with names conflicting with Object and Kernel methods #18

Conversation

ElvinEfendi
Copy link
Contributor

@ElvinEfendi ElvinEfendi commented Feb 21, 2024

We currently rely on method_missing to provide attr readers for the fields. However, when a field has the same name as one of base Ruby methods such as method, test, method_missing method isn't triggered and instead Ruby invokes the corresponding base method. One can avoid this by to_hing first and then accessing the data like .to_h["method"].

This PR tries to implement the same access pattern for such conflicting keywords.

I am not fully convinced this is the right approach (overriding base methods and turning them into field accessor feels wrong), what if we explicitly define methods for all fields instead of relying on method_missing? Thinking out loud, while implementation-wise it would be improvement, we would still be overriding base methods. Alternatively, we can introduce a convention where we append _value suffix when there's a conflict, what do you think?

EDIT: on a second thought, seems like it's common in Ruby to override pre-existing methods like this?

@@ -243,7 +243,7 @@ def test_define_simple_query_result
GRAPHQL

response = @client.query(Temp::Query)
refute response.data.me.respond_to?(:name)
assert response.data.me.respond_to?(:name)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

this feels wrong, I'll try to understand why this behaved differently when method is explicitly defined

@ElvinEfendi
Copy link
Contributor Author

@rmosolgo what do you think?

@ElvinEfendi
Copy link
Contributor Author

Maybe it'd be even simpler if we just deleted the methods with self.singleton_class.undef_method(:name) whenever :name conflicts with an existing method? That way everything can go through the method_missing path.

@ElvinEfendi ElvinEfendi marked this pull request as ready for review February 22, 2024 13:15
@ElvinEfendi ElvinEfendi force-pushed the support-fields-same-as-ancestor-methods branch from e97cb5b to b7b12e6 Compare February 22, 2024 13:29
@swalkinshaw
Copy link

Considering these classes are specifically created for GraphQL responses, I think it's fine to override the built-in methods. If a consumer really wanted to use these classes for other purposes (which I'm not sure is "correct") then they could build another representation from the hash.

I do think it's weird to use undef + method_missing when we could explicitly define each method which would just override the built-in methods as you said. But considering that's a bigger refactor, I'd also be okay with this implementation since it's the same end result (I think).

@rmosolgo
Copy link
Collaborator

For my part, I agree the implementation is unusual Ruby, but then again, the existing implementation is pretty weird too. There's like a re-implementation of methods and inheritance here ... for my part, I'm included to include this with the expectation that that the interface (method calls retrieve fetched graphql values) will stay intact. But I'm hoping to refactor the implementation to be more "plain Ruby" soon, as part of a performance audit.

Any objections or other considerations before continuing with that approach?

@ElvinEfendi
Copy link
Contributor Author

I'm included to include this with the expectation that that the interface (method calls retrieve fetched graphql values) will stay intact

Unless there's a strong performance or maintainability benefits of changing that interface (I can not think of one), I'd keep the interface of retrieving fetched graphql values via method calls.

@ElvinEfendi ElvinEfendi force-pushed the support-fields-same-as-ancestor-methods branch from 0e782ce to 334a841 Compare March 1, 2024 02:39
@ElvinEfendi ElvinEfendi force-pushed the support-fields-same-as-ancestor-methods branch from 334a841 to 06f8483 Compare March 1, 2024 02:40
self.class::METHODS_TO_UNDEF.each do |method_name|
begin
singleton_class.undef_method(method_name)
rescue; end
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 tried checking with single_class.method_defined?(method_name) but while it was returning true for i.e :method, it was returning false for :test. I'm not sure what else to try here to avoid the exception handling.

@ElvinEfendi
Copy link
Contributor Author

Closing this as I think this approach creates more headache than it solves and also introduces yet another magic. At our company, we instead updated the clients to use to_h interface to retrieve fields that have conflicting names with Ruby base methods.

@ElvinEfendi ElvinEfendi closed this Mar 8, 2024
@ElvinEfendi ElvinEfendi deleted the support-fields-same-as-ancestor-methods branch March 8, 2024 15:13
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.

5 participants