johnrl
johnrl

Reputation: 583

Optional argument in `File.read` with Ruby 1.9.2

According to the docs, I believe the example below with Ruby 1.9.2 should work in the same way as Ruby 1.9.3, but it doesn't. Given a file test with the contents hello:

Ruby 1.9.3p484:

File.read "test", 4, :mode => 'rb'
# => "HELL" 

Ruby 1.9.2p320

File.read "test", 4, :mode => 'rb'
# => TypeError: can't convert Hash into Integer

It seems like Ruby 1.9.2 and 1.9.3 differ in their way of handling optional args to File.read. Why? I cannot figure out where it's stated that this change was made.

Upvotes: 2

Views: 121

Answers (2)

sawa
sawa

Reputation: 168189

Complementing Holger Just's answer.

The source code actually seems to be different.

Ruby 1.9.2

               static VALUE
rb_io_s_read(int argc, VALUE *argv, VALUE io)
{
    VALUE offset;
    struct foreach_arg arg;

    rb_scan_args(argc, argv, "13", NULL, NULL, &offset, NULL);
    open_key_args(argc, argv, &arg);
    if (NIL_P(arg.io)) return Qnil;
    if (!NIL_P(offset)) {
        struct seek_arg sarg;
        int state = 0;
        sarg.io = arg.io;
        sarg.offset = offset;
        sarg.mode = SEEK_SET;
        rb_protect(seek_before_access, (VALUE)&sarg, &state);
        if (state) {
            rb_io_close(arg.io);
            rb_jump_tag(state);
        }
        if (arg.argc == 2) arg.argc = 1;
    }
    return rb_ensure(io_s_read, (VALUE)&arg, rb_io_close, arg.io);
}

Ruby 1.9.3

               static VALUE
rb_io_s_read(int argc, VALUE *argv, VALUE io)
{
    VALUE opt, offset;
    struct foreach_arg arg;

    argc = rb_scan_args(argc, argv, "13:", NULL, NULL, &offset, NULL, &opt);
    open_key_args(argc, argv, opt, &arg);
    if (NIL_P(arg.io)) return Qnil;
    if (!NIL_P(offset)) {
        struct seek_arg sarg;
        int state = 0;
        sarg.io = arg.io;
        sarg.offset = offset;
        sarg.mode = SEEK_SET;
        rb_protect(seek_before_access, (VALUE)&sarg, &state);
        if (state) {
            rb_io_close(arg.io);
            rb_jump_tag(state);
        }
        if (arg.argc == 2) arg.argc = 1;
    }
    return rb_ensure(io_s_read, (VALUE)&arg, rb_io_close, arg.io);
}

And the diff is:

diff

So they are actually different.

Upvotes: 0

Holger Just
Holger Just

Reputation: 55833

It seems like Ruby 1.9.2 also expects the offset if you specify the length (in related news, I can also reproduce this on 1.9.2p320 but not on 1.9.3p484). It is not clear to me from the documentation and the C code why this would be required on 1.9.2 but it shouldn't be too big a problem here.

You can just use pass the offset as nil and it will work fine on 1.9.3 and 1.9.2.

File.read "test", 4, nil, :mode => 'rb'

Upvotes: 1

Related Questions