Unknown encoding in ruby 1.9.1-p429 (4 comment)

Added by Christoph Kappel over 6 years ago

Updating Ruby is usually a task that breaks many things, therefore I avoid it if possible. Being archuser, the maintainer of the Ruby decides when I need to face new problems.

This time they just added filesystem encoding stuff in p429:

Error loading gem paths on load path in gem_prelude
unknown encoding name - filesystem
<internal:gem_prelude>:69:in `find'
<internal:gem_prelude>:69:in `set_home'
<internal:gem_prelude>:38:in `dir'
<internal:gem_prelude>:76:in `set_paths'
<internal:gem_prelude>:47:in `path'
<internal:gem_prelude>:286:in `push_all_highest_version_gems_on_load_path'
<internal:gem_prelude>:355:in `<compiled>'

There exists still now sane way to init the gem path of Ruby so we can start with our hack from Setup gems load path in embedded ruby and add the required encoding stuff. This problem is known in the ruby redmine and the changesets to this issue give at least some hints what need to be done:

Numbers: on /off 1 #include <ruby/encoding.h>
 3 VALUE encoding = Qnil;
 5 void Init_prelude(void);
 8 ruby_init();
 9 ruby_init_loadpath();
10 ruby_script("subtle");
12 /* Get encoding from file system */
13 encoding = rb_enc_from_encoding(rb_filesystem_encoding());
15 /* Set internal and external encoding */
16 rb_enc_set_default_internal(encoding);
17 rb_enc_set_default_external(encoding);
19 /* FIXME: Fake ruby_init_gems(Qtrue) */
20 rb_define_module("Gem");
21 Init_prelude();

Inheritance of Ruby classes defined in C

Added by Christoph Kappel almost 7 years ago

Creating a Ruby class directly via C is really painless, under normal circumstances you don't even have to worry about the alloc stuff. Just supply a #initialize when needed and you are done.

Numbers: on /off1 klass = rb_define_class("Test", rb_cObject);
2 rb_define_method(klass, "initialize", TestInit, 0);

Inheritance in this case works like a charm, your initialize will be called like expected. It get's more tricky when you need to call Data_Wrap_Struct. The function itself allocates an object and returns it with your supplied data pointer attached.

First solution you will most likely come up with is defining your own .new like this:

Numbers: on /off1 klass = rb_define_class("Test", rb_cObject);
2 rb_define_singleton_method(klass, "new", TestNew, -1);

Since we overwrite the default .new we need to do the call to #initialize by ourself:

Numbers: on /off 1 VALUE
 2 TextNew(int argc,
 3   VALUE *argv,
 4   VALUE self)
 5 {
 6   VALUE instance = Qnil;
 7   void *ptr = NULL;
 9   /* Fill ptr with something reasonable */
11   instance = Data_Wrap_Struct(self, NULL, NULL, ptr);
13   /* Pass arguments to initialize */
14   rb_obj_call_init(instance, argc, argv);
16   return instance;
17 }

This will wrap our data pointer and call #initialize with all arguments - no problems so far.

Problems start when someone wants to inherit from this class:

Numbers: on /off1 class AnotherTest < Test
2   :attr_reader = arg3
4   def initialize(arg1, arg2, arg3)
5     super(arg1, arg2)
6     @var = arg3
7   end
8 end

Now rb_obj_call_init calls the default #initialize and not the one from the inherited class - this will most likely end with an ArgumentError.

Remember that the first version just worked and Data_Wrap_Struct creates a new object - so we can't call it in #initialize where self is immutable.

The solution is easy, don't create your own .new. We can use allocate to just allocate our object, let Ruby do the dirty stuff and rely on it to call our #initialize.

In C it looks like this:

Numbers: on /off 1 klass = rb_define_class("Test", rb_cObject);
 3 /* This defines our alloc function, slightly changed syntax */
 4 rb_define_alloc_func(klass, TestAlloc);
 6 rb_define_method(klass, "initialize", TestInit, 0);
 9 TestAlloc(VALUE self)
10 {
11   VALUE instance = Qnil;
12   void *ptr = NULL;
14   /* Fill ptr with something reasonable */
16   instance = Data_Wrap_Struct(self, NULL, NULL, ptr);
18   return instance;
19 }
22 TestInit(VALUE self)
23 {
24   /* Do initialization here */
26   return Qnil;
27 }

Setup gems load path in embedded ruby (1 comment)

Added by Christoph Kappel about 7 years ago

Ruby lacks a way to proper init the load path for gems without using stuff like ruby_options(), which will make things even worse. Generally this is, because Ruby wasn't developed to be embedded in the first place, that also explains the lack of docs about this topic.

Reading through the code these lines here are exactly what we are looking for:

Numbers: on /off 1 /* ruby.c:1055: */
 2 void Init_prelude(void);
 4 static void                                            
 5 ruby_init_gems(int enable)                                
 6 {
 7   if (enable) rb_define_module("Gem");                                   
 8   Init_prelude();                                             
 9 }

Looks good, but ruby_init_gems() is static and thus not accessable by third party like us. The workaround is to mimic the function in our code, this includes the forward declaration of Init_prelude() or our compiler will complain about it.

In subtle I use the following to init the vm:

Numbers: on /off 1 void Init_prelude(void);
 4 ruby_init();
 5 ruby_init_loadpath();
 6 ruby_script("subtle");
 8 /* FIXME: Fake ruby_init_gems(Qtrue) */
 9 rb_define_module("Gem");
10 Init_prelude();

If you insist to know what Init_prelude() really does have a look at the miniprelude.c file of the Ruby tarball.

Also available in: Atom RSS