1

I'm trying to do something like this but I'm having trouble understanding how to use Ruby internals in my C code.

static VALUE func_get_date_object(VALUE self, VALUE vdate){
VALUE rb_date;
VALUE date;
rb_date = rb_funcall(rb_intern("Date"), rb_intern("new"), 0);;
date = rb_funcall(rb_date, rb_intern("parse"), 0);
return date;
}

What I want to do is pass in the vdate as a string like you would for Date.parse('yyyy-mm-dd')

But first I think that I need to know how to create or instantiate a new Date class object in C for Ruby. How may I do this please?

I have a test written for that code that does this.

def test_date
  assert_equal('', @t.date(@t_date_str))
end

Output is

NoMethodError: undefined method `new' for 18709:Fixnum
Douglas G. Allen
  • 2,203
  • 21
  • 20
  • That code is equivalent to `Date.new.parse` which is probably not what you want. – tadman Jan 09 '17 at 18:18
  • That code does not even compile so what I want is just the new Date object so that I can use it accordingly. I've been looking and looking for some kind of example but no luck so even just a link would be cool. – Douglas G. Allen Jan 09 '17 at 19:49
  • Well wait it does compile but my test has NoMethodError: undefined method `new' for 18709:Fixnum – Douglas G. Allen Jan 09 '17 at 19:53

2 Answers2

3

rb_intern returns the internal ID for the name "Date". What you want is the actual class associated with this name, and you can get that with rb_const_get:

VALUE cDate = rb_const_get(rb_cObject, rb_intern("Date"));

You can then use this with rb_funcall to create a new instance of the Date class:

rb_date = rb_funcall(cDate, rb_intern("new"), 0);

Since it looks like you actually want to call the Date.parse class method, what you probably want to do is call parse directly on the class:

VALUE parsed = rb_funcall(cDate, rb_intern("parse"), 1, rb_str_new_cstr("2017-1-9"));
matt
  • 78,533
  • 8
  • 163
  • 197
  • Yep! That looks like what I want. Thanks! And you're pointing me in the right direction here as I wasn't sure what docs to take a look at. Thanks again Matt. – Douglas G. Allen Jan 09 '17 at 21:11
  • One caveat is that it all worked as long as I require 'date' somewhere in the Ruby code when I call that method. But as soon as I remove the date lib I get `date': uninitialized constant Date (NameError). So I'm not done. The goal here is to let the C code initialize the Date class. Any updates? – Douglas G. Allen Jan 10 '17 at 04:55
  • Well Matt now how do I require the Date library in my C code? Should I start a new question? We'll just see for now if anyone reads these comments before I do so. But thanks for your answer. – Douglas G. Allen Jan 10 '17 at 07:18
  • @DouglasG.Allen requiring from C is simply `rb_require("date");`. – matt Jan 10 '17 at 14:02
0

Yes thanks to Matt I now have:

/*
* call-seq:
*  date('yyyy-mm-dd')
*
* convert input string to Date object.
*
*/
static VALUE func_get_date(VALUE self, VALUE vdate){
  VALUE cDate = rb_const_get(rb_cObject, rb_intern("Date"));
  VALUE parsed = rb_funcall(cDate, rb_intern("parse"), 1, vdate);
  return parsed;
}

And the test is:

class TestCalcSun300 < Test::Unit::TestCase # MiniTest::Test
  def setup
    @t = CalcSun.new
    @t_date_str = '2000-01-01'
    @t_date = Date.parse('2000-01-01')
  end

  def test_date
    assert_equal(@t_date, @t.date(@t_date_str))
  end
end

Works great as long as I require 'date' in my Ruby code. But without that I do not have any Date class initialized. :-( Oh well, I'm learning.

This is for a Ruby gem that is still in development but I'll share it in case someone wants to play around with it. The original gem is good but it does not have all the latest features. The name is the same on rubygems.org

calc_sun

Douglas G. Allen
  • 2,203
  • 21
  • 20