Is there any reason to use this **options or options namedtuple/hash at all? I’d say it would be more idiomatic to just have your constructor define all these properties as their own parameters/ivars and call it a day. Something like:
module Foo
class Client
@uri : URI
@base_uri : String
@password : String?
@user : String?
@open_timeout : Int32
@read_timeout : Int32
def initialize(
uri : String,
@user : String? = nil,
@password : String? = nil,
@open_timeout : Int32 = 2,
@read_timeout : Int32 = 5
)
@uri = URI.parse(uri)
@base_uri = "#{@uri.scheme}://#{@uri.host}:#{@uri.port}"
end
end
end
Could also argue you dont really need base_uri, user, or password as you can just get all that off the uri. Might also be a good idea to allow passing a Uri instance instead of a string. Supporting both would be helpful I’d say.
hey thank you for your answer. Yes, I’m trying to learn the idiomatic way of writing crystal. In Ruby we normally have the “optional options” being passed in a hash, having an empty hash as default, to avoid having to deal with nils… Because I’m trying to port a ruby code, I’m confronting a ruby code and:
a) trying to translate it “compilable” crystal code
b) trying to make it crystal idiomatic
c) avoiding to change the API as much as possible
My dream would be be able to keep the same documentation and integration test for sake of backward compatibility and quality assurance, if possible.
My 2 cents on this is basically while Ruby is syntactically similar to Crystal, they are ultimately different languages. You definitely can port things 1:1, but IMO that is usually not the best option since you’re treating Crystal as just compiled Ruby and inheriting all of Ruby’s issues/patterns when there may be better ways to go about it.
Like to me your three goals are somewhat mutually exclusive. Like can you really make everything idiomatic Crystal without changing the API by just porting things from Ruby 1:1? Maybe, but might be better to take a step back and try and figure out what problem that Ruby code is solving and then implementing something in Crystal to solve that problem, using the tools and features of Crystal to do so.
E.g. Crystal has overloads so you could prob make a def self.new(uri : String, options : Hash): self that consumes your hash, and forwards them to the new/better API. Then you get the best of both worlds.
From my point of view, “optional options” is the worst things ever in Ruby/Rails which caused by the bad design of named arguments(@matz regrets this always, although, partly fixed on Ruby 3).
yes, it was “fixed” with keyword arguments, which is already present in ruby 2… but not every code that we write or maintain is able to be changed as we want…