In practice these sorts of type problems are actually pretty rare. If you want to be paranoid (since they really are you to get you), you can send your input through to_s
to ensure that you always have a string:
def initialize(my_string)
@my_string = my_string.to_s
end
Then you can say MyString.new(6)
and everything will work as expected. Of course, you can say MyString.new([6, 11])
and get nonsense.
If you really want my_string
to be a String you wouldn't explicitly check its class
. That will cause problems if someone has subclassed String so you'd want to at least use is_a?
:
def initialize(myString)
raise TypeError, ... unless myString.is_a? String
@myString = myString
end
There's also a to_str
method you could check:
def initialize(myString)
raise TypeError, ... unless myString.respond_to? :to_str
@myString = myString.to_str
end
Implementing that method would (in some circles) indicate that your thing is String-like enough to be a String. I think calling to_s
would be a better idea though, that would make things behave more as people would expect in Ruby.
As far as your mutator problem is concerned:
st2.my_string = ["an array!"]
You don't have to let anyone write anything they want into your properties: classes aren't structs. You could only automatically define the accessor and write your own mutator to automatically slip in your to_s
call:
class MyString
attr_reader :my_string
def initialize(my_string)
self.my_string = my_string
end
def my_string=(s)
@my_string = s.to_s
end
def uppercase_my_string
@my_string.upcase
end
end
Basically, you don't worry about types that much in Ruby, you worry about what methods something responds to. And, if you want something specifically to be a String, you make it a String by calling the universal to_s
method (which is what string interpolation, "#{x}"
, will do).