The last line, prs.field
(obviously) does not work. Can I somehow make call a method where the name of the method comes from a variable?
struct Person
getter name : String
getter email : String
def initialize(@name, @email)
end
end
prs = Person.new("Foo Bar", "foo@bar.com")
p! prs
p! prs.name
p! prs.email
["name", "email"].each {|field|
#puts field
prs.field
}
No, Crystal doesn’t have dynamic method invocation. Your best best would be doing something like:
case field
when "name" then prs.name
when "email" then prs.email
end
or something along those lines. However I’d argue theres probably a better way…
At runtime this is not possible. But it would work with a compile time variable in a macro:
{% for field in ["name", "email"] %}
prs.{{ field.id }}
{% end %}
1 Like
Combining the two, you could use a macro that iterates over the ivars of a type to construct the case. This ofc only would work in the context of a method and assuming that you never need to do like prs.some_obj.some_value
.
Yeah, this solution with a macro works really well.
Out of curiosity, what are you trying to do that uses this? Because ofc the macro could is literally the same as if you just did:
prs.name
prs.email
Then you don’t even need any macro code or anything…
EDIT: I.e. given that you need to manually specify what values to read in the macro, why not just manually call those methods on the object?
There are 4 attributes in my current real code and I run a regex on all 4 for a test to verify the values look good. I could of course do what you suggest and explicitly write down all 4, but it seemed to be too repetitive.
I dunno, hardly seems worth it to bring in macros just to save typing 4 lines. But I don’t know the real context so if that helps then
. Do remember readability/simplicity go a long way in maintainability.
1 Like