Issue #1081 has been updated by Yusuke Endoh.


Hello,

2011/5/31 Shota Fukumori <sorah / tubusu.net>:
> Patch has updated, Now File.binwrite accepts Hash for specifying options:
>
> https://gist.github.com/69c544ec245f3a07aabd

Great!  I tested your patch and noticed no problem.
Roger and rubyspec folks, could you also check it?

In terms of maintainability, I think that it would be better to
define a common function for rb_io_s_write and rb_io_s_binwrite:

http://www.atdot.net/sp/view/pw92ml



diff --git a/io.c b/io.c
index 4e1945c..25e0974 100644
--- a/io.c
+++ b/io.c
@@ -805,6 +805,12 @@ struct binwrite_arg {http://www.atdot.net/sp/view/pw92ml
     long length;
 };
 
+struct write_arg {
+    VALUE io;
+    VALUE str;
+    int nosync;
+};
+
 static VALUE
 io_binwrite_string(VALUE arg)
 {
@@ -8366,6 +8372,124 @@ rb_io_s_binread(int argc, VALUE *argv, VALUE io)
     return rb_ensure(io_s_read, (VALUE)&arg, rb_io_close, arg.io);
 }
 
+static VALUE
+io_s_write0(struct write_arg *arg)
+{
+    return io_write(arg->io,arg->str,arg->nosync);
+}
+
+static VALUE
+io_s_write(int argc, VALUE *argv, int binary)
+{
+    VALUE string, offset, opt;
+    struct foreach_arg arg;
+    struct write_arg warg;
+
+    rb_scan_args(argc, argv, "21:", NULL, &string, &offset, &opt);
+
+    if (NIL_P(opt)) opt = rb_hash_new();
+    else opt = rb_hash_dup(opt);
+
+
+    if (NIL_P(rb_hash_aref(opt,sym_mode))) {
+	int mode = O_WRONLY|O_CREAT;
+#ifdef O_BINARY
+	if (binary) mode |= O_BINARY;
+#endif
+	if (NIL_P(offset)) mode |= O_TRUNC;
+	rb_hash_aset(opt,sym_mode,INT2NUM(mode));
+    }
+    open_key_args(argc,argv,opt,&arg);
+
+#ifndef O_BINARY
+    if (binary) rb_io_binmode_m(arg.io);
+#endif
+
+    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);
+	}
+    }
+
+    warg.io = arg.io;
+    warg.str = string;
+    warg.nosync = 0;
+
+    return rb_ensure(io_s_write0, (VALUE)&warg, rb_io_close, arg.io);
+}
+
+/*
+ *  call-seq:
+ *     IO.write(name, string, [offset] )   => fixnum
+ *     IO.write(name, string, [offset], open_args )   => fixnum
+ *
+ *  Opens the file, optionally seeks to the given <i>offset</i>, writes
+ *  <i>string</i>, then returns the length written.
+ *  <code>write</code> ensures the file is closed before returning.
+ *  If <i>offset</i> 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)
+{
+    io_s_write(argc, argv, 0);
+}
+
+/*
+ *  call-seq:
+ *     IO.binwrite(name, string, [offset] )   => fixnum
+ *
+ *  Opens the file, optionally seeks to the given <i>offset</i>, write
+ *  <i>string</i> then returns the length written.
+ *  <code>binwrite</code> ensures the file is closed before returning.
+ *  The open mode would be "wb:ASCII-8BIT".
+ *  If <i>offset</i> 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)
+{
+    io_s_write(argc, argv, 1);
+}
+
 struct copy_stream_struct {
     VALUE src;
     VALUE dst;
@@ -10315,6 +10439,8 @@ Init_IO(void)
     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);
diff --git a/test/ruby/test_io.rb b/test/ruby/test_io.rb
index f919227..8aaecd1 100644
--- a/test/ruby/test_io.rb
+++ b/test/ruby/test_io.rb
@@ -1861,4 +1861,61 @@ End
     end
   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))
+    File.delete t
+    assert_equal(3, File.write(path, "foo", encoding: "UTF-8"))
+    File.delete t
+    assert_equal(3, File.write(path, "foo", 0, encoding: "UTF-8"))
+    assert_equal("foo", File.read(path))
+    assert_equal(1, File.write(path, "f", 1, encoding: "UTF-8"))
+    assert_equal("ffo", File.read(path))
+    File.delete t
+    assert_equal(1, File.write(path, "f", 1, encoding: "UTF-8"))
+    assert_equal("\00f", File.read(path))
+    assert_equal(1, File.write(path, "f", 0, encoding: "UTF-8"))
+    assert_equal("ff", 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) }
+    assert_nothing_raised(TypeError) { File.binwrite(path, "string", mode: "w", encoding: "EUC-JP") }
+    t.unlink
+  end
 end

-- 
Yusuke Endoh <mame / tsg.ne.jp>
----------------------------------------
Feature #1081: add File::write() convenience method
http://redmine.ruby-lang.org/issues/1081

Author: Suraj Kurapati
Status: Assigned
Priority: Normal
Assignee: Yusuke Endoh
Category: core
Target version: 1.9.3


=begin
 Please add a File::write() convenience method to the core Ruby API.
 
 Currently, it is easier to read whole files than to write them:
 
   # reading a whole file --- less effort
   text = File::read('foo.txt')
 
   # writing a whole file --- more effort
   File::open('foo.txt', 'wb') {|f| f.write 'ruby!' }
 
 This imbalance can be corrected by adding a File::write method,
 such as the following, to the core Ruby API:
 
   class File
     def self.write path, data, mode = 'wb'
       open(path, mode) {|f| f.write data }
     end
   end
 
 Thanks for your consideration.
=end



-- 
http://redmine.ruby-lang.org