kddeisz kddeisz - 6 months ago 11
Ruby Question

Convert values from symbol to string in ruby

I'm writing a gem, and one of the pain points is my

camelize
method. I started rewriting it in C and it's working with the below implementation:

#include <ctype.h>
#include <ruby.h>

VALUE MyGem = Qnil;
VALUE Utils = Qnil;

VALUE camelize(VALUE self, VALUE str)
{
char *orig_str = rb_string_value_cstr(&str);
char new_str[strlen(orig_str)];

char prev;
int orig_idx = 0, new_idx = 0;

new_str[0] = toupper(orig_str[0]);
for (orig_idx = 1, new_idx = 1; orig_str[orig_idx] != '\0'; orig_idx++) {
if (prev == '_') {
new_str[new_idx++] = toupper(orig_str[orig_idx]);
}
else if (orig_str[orig_idx] != '_') {
new_str[new_idx++] = orig_str[orig_idx];
}
prev = orig_str[orig_idx];
}

return rb_str_new(new_str, new_idx);
}

void Init_my_gem()
{
MyGem = rb_define_module("MyGem");
Utils = rb_define_module_under(MyGem, "Utils");
rb_define_singleton_method(Utils, "camelize", camelize, 1);
}


The only problem is, the
VALUE str
can be either a symbol or a string. Does anyone know how to convert the
str
value to a string before I pull it out into a
char*
?

Answer

The simplest way is just to call to_s on str (rb_string_value_cstr will try to use to_str, put not to_s):

str = rb_funcall(str, rb_intern("to_s"), 0);

If you want this only to work for symbols, you could check for them explicitly. In this case you could call the function that implements to_s for symbols directly if you want (if you did this it would always use the original Symbol#to_s even if you open the Symbol class and replaced it with a different implementation):

if (TYPE(str) == T_SYMBOL) {
  str = rb_sym_to_s(str);
}

Another option is to use rb_convert_type with the to_s method, which is (as far as I can tell) pretty much equivalent to just calling to_s:

str = rb_convert_type(str, T_STRING, "String", "to_s");