Lexical Casts with Ruby

Hello,
I want to assign some values I recive in string form to attributes of a
user specified class. The problem is that I have to lexical cast the
strings into the required type. I can't use to_i or to_f because the attribute
type can only be determined at runtime.

Is there a way for lexical casts between types in Ruby? Something like
convert("10", Fixnum)
=> 10

Regards,
Rüdiger Sonderfeld <kingruedi@c-plusplus.de>

Well, there are several options:

You can use send, like your_string.send(:to_i) where you determine the
symbol (:to_i, :to_f etc) at runtime.

You can use case

result = case your_string
  when /^\d+$/ then your_string.to_i
  when /^\d.\.\d+/ then your_string.to_f
  else your_string
end

Or you put conversions into a map

conv = {
Fixnum => lambda {|x| x.to_i},
Float => lambda {|x| x.to_f},
}

result = conv[Fixnum][your_string]

Hope, that gets you started.

Kind regards

robert

···

2005/9/3, Rüdiger Sonderfeld <kingruedi@c-plusplus.de>:

Hello,
I want to assign some values I recive in string form to attributes of a
user specified class. The problem is that I have to lexical cast the
strings into the required type. I can't use to_i or to_f because the attribute
type can only be determined at runtime.

Is there a way for lexical casts between types in Ruby? Something like
convert("10", Fixnum)
=> 10

Hi --

Hello,
I want to assign some values I recive in string form to attributes of a
user specified class. The problem is that I have to lexical cast the
strings into the required type. I can't use to_i or to_f because the attribute
type can only be determined at runtime.

Is there a way for lexical casts between types in Ruby? Something like
convert("10", Fixnum)
=> 10

See Robert's answer. Also, another possibility would be to put the
knowledge directly in the object:

   class MyClass
     def x=(s)
       @x = s.to_i
     end

     def y=(s)
       @y = s.to_f
     end

     # ...
   end

That way, you can just do:

   obj.x = "10"

and have the conversion be encapsulated in the object.

If using the assignment syntax seems *too* transparent (since you're
not really setting it to "10"), you could use differently-named
methods. But the principle would be the same: put the knowledge in
the object.

David

···

On Sun, 4 Sep 2005, [utf-8] Rüdiger Sonderfeld wrote:

--
David A. Black
dblack@wobblini.net

In C++, boost::lexical_cast uses streams to convert the object to a
string and then from the string to the desired type.

It is easy to do the first part in ruby; all objects have a to_s method.

The second part is harder. There is no uniform mechanism for converting
a string into an object.

Paul

···

On Sun, Sep 04, 2005 at 12:16:26AM +0900, R??diger Sonderfeld wrote:

Hello,
I want to assign some values I recive in string form to attributes of a
user specified class. The problem is that I have to lexical cast the
strings into the required type. I can't use to_i or to_f because the attribute
type can only be determined at runtime.

Is there a way for lexical casts between types in Ruby? Something like
convert("10", Fixnum)
=> 10

A while back, I suggested adding some klass.from_* methods.
For example:

def Integer.from_s(s)
  s.to_i;
end

Any class that could make one of its objects from a String
would put this method in. You could do the same for other
classes to convert from also: from_i, from_f, etc. Here would
be the usage:

Integer.from_s("10") # 10
10.to_s # "10" - nothing new

As a convienence you could also add this:

class String
  def to(klass)
    klass.from_s(self)
  end
  def self.from(obj)
    obj.to_s
  end
end

so you could do:

"10".to(Integer) # 10
String.from(10) # "10"

If this is a good solution for you, maybe an RCR for this more
general conversion mechanism is in order.

···

--- Paul Brannan <pbrannan@atdesk.com> wrote:

On Sun, Sep 04, 2005 at 12:16:26AM +0900, R??diger Sonderfeld > wrote:
> Hello,
> I want to assign some values I recive in string form to
attributes of a
> user specified class. The problem is that I have to lexical
cast the
> strings into the required type. I can't use to_i or to_f
because the attribute
> type can only be determined at runtime.
>
> Is there a way for lexical casts between types in Ruby?
Something like
> convert("10", Fixnum)
> => 10

In C++, boost::lexical_cast uses streams to convert the
object to a
string and then from the string to the desired type.

It is easy to do the first part in ruby; all objects have a
to_s method.

The second part is harder. There is no uniform mechanism for
converting
a string into an object.

______________________________________________________
Click here to donate to the Hurricane Katrina relief effort.
http://store.yahoo.com/redcross-donate3/

See RCR#280. The implementation is 34 lines long.

Paul

···

On Thu, Sep 08, 2005 at 02:09:32AM +0900, Eric Mahurin wrote:

so you could do:

"10".to(Integer) # 10
String.from(10) # "10"

If this is a good solution for you, maybe an RCR for this more
general conversion mechanism is in order.

Don't know why I missed that one. I don't really like the
implementation with the global (or rather class variable) hash.
I would rather see an encapsulated API - each class gives
methods for converting to/from objects of other classes of
interest (i.e. String). You may have duplicated code or one
from_* calling another to_* (or vice-versa), but so what. This
way requires 0 lines of overhead - nothing needs to manage
framework. It's simple and is an extension of what we already
have. The klass#to(toKlass) and klass.from(fromObj) are
shortcuts to get a little more abstract if you want:

class String
  def self.from(obj); obj.to_s; end
  def to(klass); klass.from_s(self); end
end

If you did this, then I guess you could consider this the
overhead for each class that other classes want to convert
to/from - String, Integer, Float, Array, etc.

I would think this more straight-forward approach would be more
likely accepted.

···

--- Paul Brannan <pbrannan@atdesk.com> wrote:

On Thu, Sep 08, 2005 at 02:09:32AM +0900, Eric Mahurin wrote:
> so you could do:
>
> "10".to(Integer) # 10
> String.from(10) # "10"
>
> If this is a good solution for you, maybe an RCR for this
more
> general conversion mechanism is in order.

See RCR#280. The implementation is 34 lines long.

______________________________________________________
Click here to donate to the Hurricane Katrina relief effort.
http://store.yahoo.com/redcross-donate3/

Eric Mahurin ha scritto:

If this is a good solution for you, maybe an RCR for this

more

general conversion mechanism is in order.

See RCR#280. The implementation is 34 lines long.

Don't know why I missed that one. I don't really like the
implementation with the global (or rather class variable) hash.
I would rather see an encapsulated API - each class gives
methods for converting to/from objects of other classes of
interest (i.e. String).

<snip>

The reason for the global transformation table is basically being able to handle any kind of "type" not just Classes. I like to think of ruby as object based more than Class based.

Anyway, even if your approach is more conservative, I'd appreciate if it was blessed[1].

I wonder what matz think of this stuff.

[1]
what I'd really like to see is multimethods. Just specialize #new and everything goes fine :wink:

Would you be more amenable to a solution like:

  class Object
    def to(type, *args, &block)
      return send("to_#{type}", *args, &block)
    end

    def to_String
      return to_s
    end
  end

  class String
    def to_Integer
      return Integer(self)
    end

    # etc.
  end

This eliminates the global constant by using double-dispatch, plus
retains the ability to convert to a non-class type (e.g.
foo.to(Enumerable)).

Paul

···

On Fri, Sep 09, 2005 at 03:49:59AM +0900, Eric Mahurin wrote:

class String
  def self.from(obj); obj.to_s; end
  def to(klass); klass.from_s(self); end
end

If you did this, then I guess you could consider this the
overhead for each class that other classes want to convert
to/from - String, Integer, Float, Array, etc.

I would think this more straight-forward approach would be more
likely accepted.

Let's say you have some aribitrary class that you want to
convert to/from a string. With the above, you'd put the
to-string method in this class and the from-string method in
String. I think better encapsulation would be to put both of
these in this new aribitrary class. To do it this way and
force the method names to have the class name in them, you'd do
this:

class Object
  def self.from(obj,*args,&block)
    send("from_#{self}".to_sym,obj,*args,&block)
  rescue
    obj.send("to_#{self}".to_sym,*args,&block)
  end
  def to(klass,*args,&block)
    send("to_#{klass}".to_sym,*args,&block)
  rescue
    klass.send("from_#{self.class}".to_sym,self,*args,&block)
  end
  def to_String
    to_s
  end
  def to_Integer
    to_i
  end
end

class Xyz
  def self.from_String(s,base=10)
    ... make a Xyz from s ...
  end
  def to_String(base=10)
    ... make a String from self ...
  end
end

Of course in the above, "klass" doesn't have to be a Class, but
it does need to respond to the right from_* method.

I'm not sure of the value of using from_String/to_String over
from_s/to_s. I think you'll have just a few classes that
you'll have many classes converting from/to them. You'll
never be able to convert from one arbitrary class to another.
Also, the above code is kind of ugly forming method names and
trying two different methods (not very duck-type like) - but I
could get over it.

Would this work with the non-class types you are talking about?

···

--- Paul Brannan <pbrannan@atdesk.com> wrote:

On Fri, Sep 09, 2005 at 03:49:59AM +0900, Eric Mahurin wrote:
> class String
> def self.from(obj); obj.to_s; end
> def to(klass); klass.from_s(self); end
> end
>
> If you did this, then I guess you could consider this the
> overhead for each class that other classes want to convert
> to/from - String, Integer, Float, Array, etc.
>
> I would think this more straight-forward approach would be
more
> likely accepted.

Would you be more amenable to a solution like:

  class Object
    def to(type, *args, &block)
      return send("to_#{type}", *args, &block)
    end

    def to_String
      return to_s
    end
  end

  class String
    def to_Integer
      return Integer(self)
    end

    # etc.
  end

This eliminates the global constant by using double-dispatch,
plus
retains the ability to convert to a non-class type (e.g.
foo.to(Enumerable)).

Paul

______________________________________________________
Click here to donate to the Hurricane Katrina relief effort.
http://store.yahoo.com/redcross-donate3/