Index: gem_prelude.rb
===================================================================
--- gem_prelude.rb	(revision 28430)
+++ gem_prelude.rb	(working copy)
@@ -197,9 +197,15 @@
           requirement, version = version_requirements[0].split
           requirement.strip!
 
+          # accomodate for gem 'gem_name', '2.3.8'
+          if !version
+            version = requirement
+            requirement = '='
+          end
+
           if loaded_version = GemVersions[gem_name] then
             case requirement
-            when ">", ">=" then
+            when ">", ">=", '=' then
               return false if
                 (loaded_version <=> Gem.integers_for(version)) >= 0
             when "~>" then
Index: io.c
===================================================================
--- io.c	(revision 28430)
+++ io.c	(working copy)
@@ -7789,7 +7789,7 @@
 };
 
 static void
-open_key_args(int argc, VALUE *argv, struct foreach_arg *arg)
+open_key_args_with_opt(int argc, VALUE *argv, struct foreach_arg *arg, int mandatory_argc, int default_mode, int default_perm)
 {
     VALUE opt, v;
 
@@ -7797,9 +7797,9 @@
     arg->io = 0;
     arg->argc = argc - 1;
     arg->argv = argv + 1;
-    if (argc == 1) {
+    if (argc == mandatory_argc) {
       no_key:
-	arg->io = rb_io_open(argv[0], INT2NUM(O_RDONLY), INT2FIX(0666), Qnil);
+	arg->io = rb_io_open(argv[0], INT2NUM(default_mode), INT2FIX(default_perm), Qnil);
 	return;
     }
     opt = pop_last_hash(&arg->argc, arg->argv);
@@ -7824,9 +7824,23 @@
 	rb_ary_clear(args);	/* prevent from GC */
 	return;
     }
+    if (default_mode != O_RDONLY && NIL_P(rb_hash_aref(opt, sym_mode))) {
+	opt = rb_hash_dup(opt);
+	rb_hash_aset(opt, sym_mode, INT2NUM(default_mode));
+    }
+    if (default_perm != 0666 && NIL_P(rb_hash_aref(opt, sym_perm))) {
+	opt = rb_hash_dup(opt);
+	rb_hash_aset(opt, sym_perm, INT2FIX(default_perm));
+    }
     arg->io = rb_io_open(argv[0], Qnil, Qnil, opt);
 }
 
+static void
+open_key_args(int argc, VALUE *argv, struct foreach_arg *arg)
+{
+    open_key_args_with_opt(argc, argv, arg, 1, O_RDONLY, 0666);
+}
+
 static VALUE
 io_s_foreach(struct foreach_arg *arg)
 {
@@ -7917,6 +7931,16 @@
     return io_read(arg->argc, arg->argv, arg->io);
 }
 
+struct write_arg {
+    VALUE io, str;
+};
+
+static VALUE
+io_s_write(struct write_arg *arg)
+{
+    return io_write(arg->io, arg->str, 0);
+}
+
 struct seek_arg {
     VALUE io;
     VALUE offset;
@@ -8020,6 +8044,110 @@
     return rb_ensure(io_s_read, (VALUE)&arg, rb_io_close, arg.io);
 }
 
+static VALUE rb_io_s_write_helper(int argc, VALUE *argv, VALUE io, int use_binary);
+
+/*
+ *  call-seq:
+ *     IO.write(name, string, [offset] )   => fixnum
+ *     IO.write(name, string, [offset], open_args )   => fixnum
+ *
+ *  Opens the file, optionally seeks to the given offset, writes
+ *  string, then returns the length written.
+ *  write ensures the file is closed before returning.
+ *  If offset is not given, the file is truncated.  Otherwise,
+ *  it is not truncated.
+ *
+ *  If the last argument is a hash, it specifies option for internal
+ *  open().  The key would be the following.  open_args: is exclusive
+ *  to others.
+ *
+ *   encoding: string or encoding
+ *
+ *    specifies encoding of the read string.  encoding will be ignored
+ *    if length is specified.
+ *
+ *   mode: string
+ *
+ *    specifies mode argument for open().  it should start with "w" or "a" or "r+"
+ *    otherwise it would cause error.
+ *
+ *   perm: fixnum
+ *
+ *    specifies perm argument for open().
+ *
+ *   open_args: array of strings
+ *
+ *    specifies arguments for open() as an array.
+ *
+ *     IO.write("testfile", "0123456789")      #=> "0123456789"
+ *     IO.write("testfile", "0123456789", 20)  #=> "This is line one\nThi0123456789two\nThis is line three\nAnd so on...\n"
+ */
+
+static VALUE
+rb_io_s_write(int argc, VALUE *argv, VALUE io)
+{
+    return rb_io_s_write_helper(argc, argv, io, 0);
+}
+
+static VALUE
+rb_io_s_write_helper(int argc, VALUE *argv, VALUE io, int use_binary)
+{
+    VALUE offset;
+    struct foreach_arg arg;
+    struct write_arg warg;
+    int mode = O_WRONLY | O_CREAT, mandatory_argc;
+#ifdef O_BINARY
+    if(use_binary)
+        mode |= O_BINARY;
+#endif
+
+    rb_scan_args(argc, argv, "22", NULL, &warg.str, &offset, NULL);
+    if (!NIL_P(offset) && FIXNUM_P(offset)) {
+	mandatory_argc = 3;
+    }
+    else {
+	mode |= O_TRUNC;
+	mandatory_argc = 2;
+    }
+    open_key_args_with_opt(argc, argv, &arg, mandatory_argc, mode, 0666);
+    if (NIL_P(arg.io)) return Qnil;
+    if (!NIL_P(offset) && FIXNUM_P(offset)) {
+	struct seek_arg sarg;
+	int state = 0;
+	sarg.io = arg.io;
+	sarg.offset = offset;
+	sarg.mode = SEEK_SET;
+	rb_protect((VALUE (*)(VALUE))seek_before_access, (VALUE)&sarg, &state);
+	if (state) {
+	    rb_io_close(arg.io);
+	    rb_jump_tag(state);
+	}
+	if (arg.argc == 2) arg.argc = 1;
+    }
+    warg.io = arg.io;
+    return rb_ensure(io_s_write, (VALUE)&warg, rb_io_close, arg.io);
+}
+
+/*
+ *  call-seq:
+ *     IO.binwrite(name, string, [offset] )   => fixnum
+ *
+ *  Opens the file, optionally seeks to the given offset, write
+ *  string then returns the length written.
+ *  binwrite ensures the file is closed before returning.
+ *  The open mode would be "wb:ASCII-8BIT".
+ *  If offset is not given, the file is truncated.  Otherwise,
+ *  it is not truncated.
+ *
+ *     IO.binwrite("testfile", "0123456789")      #=> "0123456789"
+ *     IO.binwrite("testfile", "0123456789", 20)  #=> "This is line one\nThi0123456789two\nThis is line three\nAnd so on...\n"
+ */
+static VALUE
+rb_io_s_binwrite(int argc, VALUE *argv, VALUE io)
+{
+    return rb_io_s_write_helper(argc, argv, io, 1);
+}
+
 struct copy_stream_struct {
     VALUE src;
     VALUE dst;
@@ -9859,6 +9987,8 @@
     rb_define_singleton_method(rb_cIO, "readlines", rb_io_s_readlines, -1);
     rb_define_singleton_method(rb_cIO, "read", rb_io_s_read, -1);
     rb_define_singleton_method(rb_cIO, "binread", rb_io_s_binread, -1);
+    rb_define_singleton_method(rb_cIO, "write", rb_io_s_write, -1);
+    rb_define_singleton_method(rb_cIO, "binwrite", rb_io_s_binwrite, -1);
     rb_define_singleton_method(rb_cIO, "select", rb_f_select, -1);
     rb_define_singleton_method(rb_cIO, "pipe", rb_io_s_pipe, -1);
     rb_define_singleton_method(rb_cIO, "try_convert", rb_io_s_try_convert, 1);
Index: test/ruby/test_io.rb
===================================================================
--- test/ruby/test_io.rb	(revision 28430)
+++ test/ruby/test_io.rb	(working copy)
@@ -1609,4 +1609,49 @@
     t.close
     assert_raise(IOError) {t.binmode}
   end
+
+  def test_s_write
+    t = Tempfile.new("foo")
+    path = t.path
+    t.close(false)
+    File.write(path, "foo\nbar\nbaz")
+    assert_equal("foo\nbar\nbaz", File.read(path))
+    File.write(path, "FOO", 0)
+    assert_equal("FOO\nbar\nbaz", File.read(path))
+    File.write(path, "BAR")
+    assert_equal("BAR", File.read(path))
+    File.write(path, "\u{3042}", mode: "w", encoding: "EUC-JP")
+    assert_equal("\u{3042}".encode("EUC-JP"), File.read(path, encoding: "EUC-JP"))
+    File.delete t
+    assert_equal(6, File.write(path,'string',2))
+    File.delete t
+    assert_raise(Errno::EINVAL) { File.write('/tmp/nonexisting','string',-2) }
+    assert_equal(6, File.write(path, 'string'))
+    assert_equal(3, File.write(path, 'sub', 1))
+    assert_equal("ssubng", File.read(path))
+    t.unlink
+  end
+
+  def test_s_binwrite
+    t = Tempfile.new("foo")
+    path = t.path
+    t.close(false)
+    File.binwrite(path, "foo\nbar\nbaz")
+    assert_equal("foo\nbar\nbaz", File.read(path))
+    File.binwrite(path, "FOO", 0)
+    assert_equal("FOO\nbar\nbaz", File.read(path))
+    File.binwrite(path, "BAR")
+    assert_equal("BAR", File.read(path))
+    File.binwrite(path, "\u{3042}")
+    assert_equal("\u{3042}".force_encoding("ASCII-8BIT"), File.binread(path))
+    File.delete t
+    assert_equal(6, File.binwrite(path,'string',2))
+    File.delete t
+    assert_equal(6, File.binwrite(path, 'string'))
+    assert_equal(3, File.binwrite(path, 'sub', 1))
+    assert_equal("ssubng", File.binread(path))
+    assert_equal(6, File.size(path))
+    assert_raise(Errno::EINVAL) { File.binwrite('/tmp/nonexisting','string',-2) }
+    t.unlink
+  end
 end