Reputation: 94409
If I have a custom Ruby class representing some string type, as in
class MyString
end
Which functions should I implement in order to make the following use cases possible:
MyString
is expectedMyString
whenever a Ruby string is expectedMyString
value (it shouldn't matter whether I use s == t
or t == s
).I saw various interesting functions like to_s
, cmp
, ==
and eq
already, but it's not clear to me when each of them is called.
My concrete use case is that I'm writing a Ruby extension using the C API which exposes functions taking (and returning) values of a custom string type (QString
, to be precise) which my extension also registers. However, I'd like to make those custom strings behave as intuitive as possible. Unfortunately I can't just return Ruby strings from my C code since it should be possible to call Qt methods on the strings.
Upvotes: 3
Views: 1550
Reputation: 45
Since I was looking for something similar, but none of the other answers worked for me, I'll post what did work for me.
Found in this blog post which discourage the use of inheriting String and instead use simple delegator.
Inheriting from SimpleDelegator create an object which delegate everything to a string of your choice but on which you add behavior as you see fit.
class ChunkyBacon < SimpleDelegator
def initialize(content)
@content = content
super @content
end
def chunky_bacon?
@content == 'chunky_bacon'
end
end
test = ChunkyBacon.new('choco pizza') # => 'choco pizza'
test.chunky_bacon? # => false
Upvotes: 0
Reputation: 146123
There are at least three approaches:
class MyString < String; ...; end
#to_s
#to_str
Doing both #2 and #3 will make the object act very much like a real String even if it isn't a subclass.
#to_s
is an explicit converter, meaning it must appear in Ruby code to work.
#to_str
is an implicit converter, meaning the Ruby interpreter will attempt to call it when it wants a String but is given something else.
Update:
Here is an example of some fun you can have with to_str
:
begin
open 1, 'r'
rescue TypeError => e
p e
end
class Fixnum
def to_str; to_s; end
end
open 1, 'r'
When run, the first open fails with TypeError
but the second proceeds to looking for 1
.
#<TypeError: can't convert Fixnum into String>
fun.rb:9:in `initialize': No such file or directory - 1 (Errno::ENOENT)
from fun.rb:9:in `open'
Upvotes: 6
Reputation: 1490
It's not generally a good idea to subclass classes that were built by someone else in Ruby, because too many things can go wrong. (You might, for example, override an internal method without knowing it.)
1) define Mystring.to_s to get automatic conversion from a Mystring to a String.
2) Not sure what you mean by this. If you want a String method that returns a Mystring, you will have to monkey-patch String:
Class String
def to_mystring
return Mystring.new(self)
end
end
3) to get t == s (assuming s is an instance of String and t an instance of Mystring) define <=>. To get s == t you will have to monkey patch String again, though.
Upvotes: 0
Reputation: 211670
Although it's tempting to sub-class String to give it a new initialize
method that will import these QString-type strings, you may just want to tack on an extension to String that helps with the conversion so you don't have to re-implement a version of String itself.
For instance, with two methods you could pretty much have this done:
class String
def self.from_qstring(qstring)
new(...)
end
def to_qstring
# ...
end
end
Having multiple storage types for String is not going to be a problem until you start comparing them, but given that Ruby's String is quite robust, writing a work-alike is difficult.
Upvotes: 1