summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog5
-rw-r--r--NEWS19
-rw-r--r--hash.c29
-rw-r--r--lib/ostruct.rb13
-rw-r--r--object.c18
-rw-r--r--struct.c26
-rw-r--r--test/ostruct/test_ostruct.rb12
-rw-r--r--test/ruby/test_env.rb4
-rw-r--r--test/ruby/test_hash.rb26
-rw-r--r--test/ruby/test_struct.rb6
10 files changed, 156 insertions, 2 deletions
diff --git a/ChangeLog b/ChangeLog
index 5280e743ca..4ebb106b4e 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,8 @@
+Tue Apr 24 12:46:50 2012 Marc-Andre Lafortune <ruby-core@marc-andre.ca>
+
+ * hash.c, object.c, struct.c, lib/ostruct.rb: add to_h methods.
+ [Feature #6276]
+
Tue Apr 24 10:54:34 2012 NAKAMURA Usaku <usa@ruby-lang.org>
* test/drb/drbtest.rb ({DRbCore,DRbAry}#teardown}: cannot pass SIGTERM
diff --git a/NEWS b/NEWS
index e390b9f09a..55c4c41289 100644
--- a/NEWS
+++ b/NEWS
@@ -21,7 +21,13 @@ with all sufficient information, see the ChangeLog file.
* added method:
* added Enumerable#lazy method for lazy enumeration.
+ * ENV
+ * aliased method:
+ * ENV.to_h is a new alias for ENV.to_hash
+
* Hash
+ * added method:
+ * added Hash#to_h as explicit conversion method, like Array#to_a.
* extended method:
* Hash#default_proc= can be passed nil to clear the default proc.
@@ -41,11 +47,20 @@ with all sufficient information, see the ChangeLog file.
* added LoadError#path method to return the file name that could not be
loaded.
+ * NilClass
+ * added method:
+ * added nil.to_h which returns {}
+
* Signal
* incompatible changes:
* Signal.trap raises ArgumentError when :SEGV, :BUS, :ILL, :FPE, :VTALRM
are specified.
+ * Struct
+ * added method:
+ * added Struct#to_h returning values with keys corresponding to the
+ instance variable names.
+
* Time
* change return value:
* Time#to_s returned encoding defaults to US-ASCII but automatically
@@ -63,6 +78,10 @@ with all sufficient information, see the ChangeLog file.
* Net::IMAP.default_ssl_port
* Net::IMAP.default_imaps_port
+* ostruct
+ * new methods:
+ * OpenStruct#to_h converts the struct to a hash.
+
* pathname
* extended method:
* Pathname#find returns an enumerator if no block is given.
diff --git a/hash.c b/hash.c
index 1b495c788c..3427ec0eb8 100644
--- a/hash.c
+++ b/hash.c
@@ -1452,6 +1452,30 @@ rb_hash_to_hash(VALUE hash)
return hash;
}
+/*
+ * call-seq:
+ * hsh.to_h -> hsh or new_hash
+ *
+ * Returns +self+. If called on a subclass of Hash, converts
+ * the receiver to a Hash object.
+ */
+
+static VALUE
+rb_hash_to_h(VALUE hash)
+{
+ if (rb_obj_class(hash) != rb_cHash) {
+ VALUE ret = rb_hash_new();
+ if (!RHASH_EMPTY_P(hash))
+ RHASH(ret)->ntbl = st_copy(RHASH(hash)->ntbl);
+ if (FL_TEST(hash, HASH_PROC_DEFAULT)) {
+ FL_SET(ret, HASH_PROC_DEFAULT);
+ }
+ RHASH_IFNONE(ret) = RHASH_IFNONE(hash);
+ return ret;
+ }
+ return hash;
+}
+
static int
keys_i(VALUE key, VALUE value, VALUE ary)
{
@@ -3054,7 +3078,8 @@ env_index(VALUE dmy, VALUE value)
/*
* call-seq:
- * ENV.to_hash -> Hash
+ * ENV.to_hash -> hash
+ * ENV.to_h -> hash
*
* Creates a hash with a copy of the environment variables.
*
@@ -3333,6 +3358,7 @@ Init_Hash(void)
rb_define_method(rb_cHash,"rehash", rb_hash_rehash, 0);
rb_define_method(rb_cHash,"to_hash", rb_hash_to_hash, 0);
+ rb_define_method(rb_cHash,"to_h", rb_hash_to_h, 0);
rb_define_method(rb_cHash,"to_a", rb_hash_to_a, 0);
rb_define_method(rb_cHash,"inspect", rb_hash_inspect, 0);
rb_define_alias(rb_cHash, "to_s", "inspect");
@@ -3443,6 +3469,7 @@ Init_Hash(void)
rb_define_singleton_method(envtbl,"key?", env_has_key, 1);
rb_define_singleton_method(envtbl,"value?", env_has_value, 1);
rb_define_singleton_method(envtbl,"to_hash", env_to_hash, 0);
+ rb_define_singleton_method(envtbl,"to_h", env_to_hash, 0);
rb_define_singleton_method(envtbl,"assoc", env_assoc, 1);
rb_define_singleton_method(envtbl,"rassoc", env_rassoc, 1);
diff --git a/lib/ostruct.rb b/lib/ostruct.rb
index 21bda73c37..167b682fa3 100644
--- a/lib/ostruct.rb
+++ b/lib/ostruct.rb
@@ -101,6 +101,19 @@ class OpenStruct
end
#
+ # Converts the OpenStruct to a hash with keys representing
+ # each attribute (as symbols) and their corresponding values
+ # Example:
+ #
+ # require 'ostruct'
+ # data = OpenStruct.new("country" => "Australia", :population => 20_000_000)
+ # data.to_h # => {:country => "Australia", :population => 20000000 }
+ #
+ def to_h
+ @table.dup
+ end
+
+ #
# Provides marshalling support for use by the Marshal library. Returning the
# underlying Hash table that contains the functions defined as the keys and
# the values assigned to them.
diff --git a/object.c b/object.c
index 59bdea0c18..3c4445cb59 100644
--- a/object.c
+++ b/object.c
@@ -1058,6 +1058,23 @@ nil_to_a(VALUE obj)
}
/*
+ * Document-method: to_h
+ *
+ * call-seq:
+ * nil.to_h -> {}
+ *
+ * Always returns an empty hash.
+ *
+ * nil.to_h #=> {}
+ */
+
+static VALUE
+nil_to_h(VALUE obj)
+{
+ return rb_hash_new();
+}
+
+/*
* call-seq:
* nil.inspect -> "nil"
*
@@ -2896,6 +2913,7 @@ Init_Object(void)
rb_define_method(rb_cNilClass, "to_f", nil_to_f, 0);
rb_define_method(rb_cNilClass, "to_s", nil_to_s, 0);
rb_define_method(rb_cNilClass, "to_a", nil_to_a, 0);
+ rb_define_method(rb_cNilClass, "to_h", nil_to_h, 0);
rb_define_method(rb_cNilClass, "inspect", nil_inspect, 0);
rb_define_method(rb_cNilClass, "&", false_and, 1);
rb_define_method(rb_cNilClass, "|", false_or, 1);
diff --git a/struct.c b/struct.c
index 7fa50f14ee..e958a78df8 100644
--- a/struct.c
+++ b/struct.c
@@ -586,6 +586,31 @@ rb_struct_to_a(VALUE s)
return rb_ary_new4(RSTRUCT_LEN(s), RSTRUCT_PTR(s));
}
+/*
+ * call-seq:
+ * struct.to_h -> hash
+ *
+ * Returns the values for this instance as a hash with keys
+ * corresponding to the instance variable name.
+ *
+ * Customer = Struct.new(:name, :address, :zip)
+ * joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345)
+ * joe.to_h[:address] #=> "123 Maple, Anytown NC"
+ */
+
+static VALUE
+rb_struct_to_h(VALUE s)
+{
+ VALUE h = rb_hash_new();
+ VALUE members = rb_struct_members(s);
+ long i;
+
+ for (i=0; i<RSTRUCT_LEN(s); i++) {
+ rb_hash_aset(h, rb_ary_entry(members, i), RSTRUCT_PTR(s)[i]);
+ }
+ return h;
+}
+
/* :nodoc: */
VALUE
rb_struct_init_copy(VALUE copy, VALUE s)
@@ -961,6 +986,7 @@ Init_Struct(void)
rb_define_method(rb_cStruct, "inspect", rb_struct_inspect, 0);
rb_define_alias(rb_cStruct, "to_s", "inspect");
rb_define_method(rb_cStruct, "to_a", rb_struct_to_a, 0);
+ rb_define_method(rb_cStruct, "to_h", rb_struct_to_h, 0);
rb_define_method(rb_cStruct, "values", rb_struct_to_a, 0);
rb_define_method(rb_cStruct, "size", rb_struct_size, 0);
rb_define_method(rb_cStruct, "length", rb_struct_size, 0);
diff --git a/test/ostruct/test_ostruct.rb b/test/ostruct/test_ostruct.rb
index dca2382b2b..1826e40e53 100644
--- a/test/ostruct/test_ostruct.rb
+++ b/test/ostruct/test_ostruct.rb
@@ -73,4 +73,16 @@ class TC_OpenStruct < Test::Unit::TestCase
assert_raise(NoMethodError) { o[:foo] }
end
+ def test_to_h
+ h = {name: "John Smith", age: 70, pension: 300}
+ os = OpenStruct.new(h)
+ to_h = os.to_h
+ assert_equal(h, to_h)
+
+ to_h[:age] = 71
+ assert_equal(70, os.age)
+ assert_equal(70, h[:age])
+
+ assert_equal(h, OpenStruct.new("name" => "John Smith", "age" => 70, pension: 300).to_h)
+ end
end
diff --git a/test/ruby/test_env.rb b/test/ruby/test_env.rb
index 9166f4df6c..8baf762472 100644
--- a/test/ruby/test_env.rb
+++ b/test/ruby/test_env.rb
@@ -327,6 +327,10 @@ class TestEnv < Test::Unit::TestCase
assert_equal(h, ENV.to_hash)
end
+ def test_to_h
+ assert_equal(ENV.to_hash, ENV.to_h)
+ end
+
def test_reject
h1 = {}
ENV.each_pair {|k, v| h1[k] = v }
diff --git a/test/ruby/test_hash.rb b/test/ruby/test_hash.rb
index e3c92125c5..0c03bf4726 100644
--- a/test/ruby/test_hash.rb
+++ b/test/ruby/test_hash.rb
@@ -77,7 +77,7 @@ class TestHash < Test::Unit::TestCase
# From rubicon
def setup
- @cls = Hash
+ @cls ||= Hash
@h = @cls[
1 => 'one', 2 => 'two', 3 => 'three',
self => 'self', true => 'true', nil => 'nil',
@@ -631,6 +631,20 @@ class TestHash < Test::Unit::TestCase
def test_to_hash
h = @h.to_hash
assert_equal(@h, h)
+ assert_instance_of(@cls, h)
+ end
+
+ def test_to_h
+ h = @h.to_h
+ assert_equal(@h, h)
+ assert_instance_of(Hash, h)
+ end
+
+ def test_nil_to_h
+ h = nil.to_h
+ assert_equal({}, h)
+ assert_nil(h.default)
+ assert_nil(h.default_proc)
end
def test_to_s
@@ -932,4 +946,14 @@ class TestHash < Test::Unit::TestCase
assert_not_equal(h.hash, h.invert.hash, feature4262)
end
end
+
+ class TestSubHash < TestHash
+ class SubHash < Hash
+ end
+
+ def setup
+ @cls = SubHash
+ super
+ end
+ end
end
diff --git a/test/ruby/test_struct.rb b/test/ruby/test_struct.rb
index d769e47dc5..b3191902a3 100644
--- a/test/ruby/test_struct.rb
+++ b/test/ruby/test_struct.rb
@@ -263,4 +263,10 @@ class TestStruct < Test::Unit::TestCase
end
assert_equal("Insecure: can't modify #{st}::S", error.message, bug5036)
end
+
+ def test_to_h
+ klass = Struct.new(:a, :b, :c, :d, :e, :f)
+ o = klass.new(1, 2, 3, 4, 5, 6)
+ assert_equal({a:1, b:2, c:3, d:4, e:5, f:6}, o.to_h)
+ end
end