[前][次][番号順一覧][スレッド一覧]

ruby-changes:62570

From: Burdette <ko1@a...>
Date: Fri, 14 Aug 2020 03:16:42 +0900 (JST)
Subject: [ruby-changes:62570] 22fd617aa5 (master): Adding doc/dig_methods.rdoc and links to it (#3416)

https://git.ruby-lang.org/ruby.git/commit/?id=22fd617aa5

From 22fd617aa5a8dd9c8426a546e0cb8a64b45c230b Mon Sep 17 00:00:00 2001
From: Burdette Lamar <BurdetteLamar@Y...>
Date: Thu, 13 Aug 2020 13:16:27 -0500
Subject: Adding doc/dig_methods.rdoc and links to it (#3416)

Adds a full discussion of #dig, along with links from Array, Hash, Struct, and OpenStruct.

CSV::Table and CSV::Row are over in ruby/csv. I'll get to them soon.

The art to the thing is to figure out how much (or how little) to say at each #dig.

diff --git a/array.c b/array.c
index a07757f..6c667a3 100644
--- a/array.c
+++ b/array.c
@@ -8639,19 +8639,20 @@ rb_ary_one_p(int argc, VALUE *argv, VALUE ary) https://github.com/ruby/ruby/blob/trunk/array.c#L8639
 }
 
 /*
- * call-seq:
- *   ary.dig(idx, ...)                 -> object
- *
- * Extracts the nested value specified by the sequence of <i>idx</i>
- * objects by calling +dig+ at each step, returning +nil+ if any
- * intermediate step is +nil+.
+ *  call-seq:
+ *    array.dig(index, *identifiers) -> object
  *
- *   a = [[1, [2, 3]]]
+ *  Finds and returns the object in nested objects
+ *  that is specified by +index+ and +identifiers+.
+ *  The nested objects may be instances of various classes.
+ *  See {Dig Methods}[doc/dig_methods_rdoc.html].
  *
- *   a.dig(0, 1, 1)                    #=> 3
- *   a.dig(1, 2, 3)                    #=> nil
- *   a.dig(0, 0, 0)                    #=> TypeError: Integer does not have #dig method
- *   [42, {foo: :bar}].dig(1, :foo)    #=> :bar
+ *  Examples:
+ *    a = [:foo, [:bar, :baz, [:bat, :bam]]]
+ *    a.dig(1) # => [:bar, :baz, [:bat, :bam]]
+ *    a.dig(1, 2) # => [:bat, :bam]
+ *    a.dig(1, 2, 0) # => :bat
+ *    a.dig(1, 2, 3) # => nil
  */
 
 static VALUE
diff --git a/doc/dig_methods.rdoc b/doc/dig_methods.rdoc
new file mode 100644
index 0000000..366275d
--- /dev/null
+++ b/doc/dig_methods.rdoc
@@ -0,0 +1,82 @@ https://github.com/ruby/ruby/blob/trunk/doc/dig_methods.rdoc#L1
+= Dig Methods
+
+Ruby's +dig+ methods are useful for accessing nested data structures.
+
+Consider this data:
+    item = {
+      id: "0001",
+      type: "donut",
+      name: "Cake",
+      ppu: 0.55,
+      batters: {
+        batter: [
+          {id: "1001", type: "Regular"},
+          {id: "1002", type: "Chocolate"},
+          {id: "1003", type: "Blueberry"},
+          {id: "1004", type: "Devil's Food"}
+        ]
+      },
+      topping: [
+        {id: "5001", type: "None"},
+        {id: "5002", type: "Glazed"},
+        {id: "5005", type: "Sugar"},
+        {id: "5007", type: "Powdered Sugar"},
+        {id: "5006", type: "Chocolate with Sprinkles"},
+        {id: "5003", type: "Chocolate"},
+        {id: "5004", type: "Maple"}
+      ]
+    }
+
+Without a +dig+ method, you can write:
+    item[:batters][:batter][1][:type] # => "Chocolate"
+
+With a +dig+ method, you can write:
+    item.dig(:batters, :batter, 1, :type) # => "Chocolate"
+
+Without a +dig+ method, you can write, erroneously
+(raises <tt>NoMethodError (undefined method `[]' for nil:NilClass)</tt>):
+    item[:batters][:BATTER][1][:type]
+
+With a +dig+ method, you can write (still erroneously, but avoiding the exception):
+    item.dig(:batters, :BATTER, 1, :type) # => nil
+
+== Why Is +dig+ Better?
+
+- It has fewer syntactical elements (to get wrong).
+- It reads better.
+- It does not raise an exception if an item is not found.
+
+== How Does +dig+ Work?
+
+The call sequence is:
+  obj.dig(*identifiers)
+
+The +identifiers+ define a "path" into the nested data structures:
+- For each identifier in +identifiers+, calls method \#dig on a receiver
+  with that identifier.
+- The first receiver is +self+.
+- Each successive receiver is the value returned by the previous call to +dig+.
+- The value finally returned is the value returned by the last call to +dig+.
+
+A +dig+ method raises an exception if any receiver does not respond to \#dig:
+  h = { foo: 1 }
+  # Raises TypeError (Integer does not have #dig method):
+  h.dig(:foo, :bar)
+
+== What Else?
+
+The structure above has \Hash objects and \Array objects,
+both of which have instance method +dig+.
+
+Altogether there are six built-in Ruby classes that have method +dig+,
+three in the core classes and three in the standard library.
+
+In the core:
+- Array#dig: the first argument is an \Integer index.
+- Hash#dig: the first argument is a key.
+- Struct#dig: the first argument is a key.
+
+In the standard library:
+- OpenStruct#dig: the first argument is a \String name.
+- CSV::Table#dig: the first argument is an \Integer index or a \String header.
+- CSV::Row#dig: the first argument is an \Integer index or a \String header.
diff --git a/hash.c b/hash.c
index f6581ac..329aa33 100644
--- a/hash.c
+++ b/hash.c
@@ -5080,53 +5080,19 @@ rb_hash_any_p(int argc, VALUE *argv, VALUE hash) https://github.com/ruby/ruby/blob/trunk/hash.c#L5080
 
 /*
  *  call-seq:
- *    hash.dig(*keys) -> value
+ *    hash.dig(key, *identifiers) -> object
  *
- *  Returns the value for a specified object in nested objects.
- *
- *  For nested objects:
- *  - For each key in +keys+, calls method \#dig on a receiver.
- *  - The first receiver is +self+.
- *  - Each successive receiver is the value returned by the previous call to \#dig.
- *  - The value finally returned is the value returned by the last call to \#dig.
+ *  Finds and returns the object in nested objects
+ *  that is specified by +key+ and +identifiers+.
+ *  The nested objects may be instances of various classes.
+ *  See {Dig Methods}[doc/dig_methods_rdoc.html].
  *
  *  Examples:
- *    h = {foo: 0}
- *    h.dig(:foo) # => 0
- *
- *    h = {foo: {bar: 1}}
- *    h.dig(:foo, :bar) # => 1
- *
  *    h = {foo: {bar: {baz: 2}}}
+ *    h.dig(:foo) # => {:bar=>{:baz=>2}}
+ *    h.dig(:foo, :bar) # => {:bar=>{:baz=>2}}
  *    h.dig(:foo, :bar, :baz) # => 2
- *
- *  Returns +nil+ if any key is not found:
- *    h = { foo: {bar: {baz: 2}}}
- *    h.dig(:foo, :nosuch) # => nil
- *
- *  The nested objects may include any that respond to \#dig.  See:
- *  - Hash#dig
- *  - Array#dig
- *  - Struct#dig
- *  - OpenStruct#dig
- *  - CSV::Table#dig
- *  - CSV::Row#dig
- *
- *  Example:
- *    h = {foo: {bar: [:a, :b, :c]}}
- *    h.dig(:foo, :bar, 2) # => :c
- *
- *  ---
- *
- *  Raises an exception if any given key is invalid
- *  (see {Invalid Hash Keys}[#class-Hash-label-Invalid+Hash+Keys]):
- *    # Raises NoMethodError (undefined method `hash' for #<BasicObject>)
- *    h.dig(BasicObject.new)
- *
- *  Raises an exception if any receiver does not respond to \#dig:
- *    h = { foo: 1 }
- *    # Raises TypeError: Integer does not have #dig method
- *    h.dig(:foo, 1)
+ *    h.dig(:foo, :bar, :BAZ) # => nil
  */
 
 static VALUE
diff --git a/lib/ostruct.rb b/lib/ostruct.rb
index e062fbd..7192a07 100644
--- a/lib/ostruct.rb
+++ b/lib/ostruct.rb
@@ -255,26 +255,20 @@ class OpenStruct https://github.com/ruby/ruby/blob/trunk/lib/ostruct.rb#L255
     modifiable?[new_ostruct_member!(name)] = value
   end
 
-  #
   # :call-seq:
-  #   ostruct.dig(name, ...)  -> object
+  #   ostruct.dig(name, *identifiers) -> object
   #
-  # Extracts the nested value specified by the sequence of +name+
-  # objects by calling +dig+ at each step, returning +nil+ if any
-  # intermediate step is +nil+.
+  # Finds and returns the object in nested objects
+  # that is specified by +name+ and +identifiers+.
+  # The nested objects may be instances of various classes.
+  # See {Dig Methods}[doc/dig_methods_rdoc.html].
   #
+  # Examples:
   #   require "ostruct"
   #   address = OpenStruct.new("city" => "Anytown NC", "zip" => 12345)
   #   person  = OpenStruct.new("name" => "John Smith", "address" => address)
-  #
-  #   person.dig(:address, "zip")            # => 12345
-  #   person.dig(:business_address, "zip")   # => nil
-  #
-  #   data = OpenStruct.new(:array => [1, [2, 3]])
-  #
-  #   data.dig(:array, 1, 0)   # => 2
-  #   data.dig(:array, 0, 0)   # TypeError: Integer does not have #dig method
-  #
+  #   person.dig(:address, "zip") # => 12345
+  #   person.dig(:business_address, "zip") # => nil
   def dig(name, *names)
     begin
       name = name.to_sym
diff --git a/struct.c b/struct.c
index 0ff0ec3..efd6bc0 100644
--- a/struct.c
+++ b/struct.c
@@ -1328,18 +1328,21 @@ rb_struct_size(VALUE s) https://github.com/ruby/ruby/blob/trunk/struct.c#L1328
 
 /*
  * call-seq:
- *   struct.dig(key, ...)              -> object
+ *   struct.dig(key, *identifiers) -> object
  *
- * Extracts the nested value specified by the sequence of +key+
- * objects by calling +dig+ at each step, returning +nil+ if any
- * intermediate step is +nil+.
+ * Finds and returns the object in nested objects
+ * that is specified by +key+ and +identifiers+.
+ * The nested objects may be instances of various classes.
+ * See {Dig Methods}[doc/dig_methods_rdoc.html].
  *
+ * Examples:
  *   Foo = Struct.new(:a)
  *   f = Foo.new(Foo.new({b: [1, 2, 3]}))
- *
- *   f.dig(:a, :a, :b, 0)    # => 1
- *   f.dig(:b, 0)            # => nil
- *   f.dig(:a, :a, :b, :c)   # TypeError: no implicit conversion of Symbol into Integer
+ *   f.dig(:a) # => #<struct Foo a={:b=>[1, 2, 3]}>
+ *   f.dig(:a, :a) # => {:b=>[1, 2, 3]}
+ *   f.dig(:a, :a, :b) # => [1, 2, 3]
+ *   f.dig(:a, :a, :b, 0) # => 1
+ *   f.dig(:b, 0) # => nil
  */
 
 static VALUE
-- 
cgit v0.10.2


--
ML: ruby-changes@q...
Info: http://www.atdot.net/~ko1/quickml/

[前][次][番号順一覧][スレッド一覧]