Repository: goby-lang/goby
Pull request #866 (2020-11-04 15:26:48)

Add missing delete / options method to http module

codecov[bot]: # [Codecov](https://codecov.io/gh/goby-lang/goby/pull/866?src=pr&el=h1) Report
> :exclamation: No coverage uploaded for pull request base (`master@1dd1905`). [Click here to learn what that means](https://docs.codecov.io/docs/error-reference#section-missing-base-commit).
> The diff coverage is `29.62%`.

[![Impacted file tree graph](https://codecov.io/gh/goby-lang/goby/pull/866/graphs/tree.svg?width=650&height=150&src=pr&token=giIKD13jMt)](https://codecov.io/gh/goby-lang/goby/pull/866?src=pr&el=tree)

```diff
@@            Coverage Diff            @@
##             master     #866   +/-   ##
=========================================
  Coverage          ?   80.52%           
=========================================
  Files             ?       54           
  Lines             ?     6829           
  Branches          ?        0           
=========================================
  Hits              ?     5499           
  Misses            ?     1091           
  Partials          ?      239           
```


| [Impacted Files](https://codecov.io/gh/goby-lang/goby/pull/866?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [vm/http.go](https://codecov.io/gh/goby-lang/goby/pull/866/diff?src=pr&el=tree#diff-dm0vaHR0cC5nbw==) | `65.38% <29.62%> (ø)` | |
| [vm/decimal.go](https://codecov.io/gh/goby-lang/goby/pull/866/diff?src=pr&el=tree#diff-dm0vZGVjaW1hbC5nbw==) | `96.12% <0.00%> (ø)` | |
| [vm/null.go](https://codecov.io/gh/goby-lang/goby/pull/866/diff?src=pr&el=tree#diff-dm0vbnVsbC5nbw==) | `87.80% <0.00%> (ø)` | |
| [vm/integer.go](https://codecov.io/gh/goby-lang/goby/pull/866/diff?src=pr&el=tree#diff-dm0vaW50ZWdlci5nbw==) | `62.71% <0.00%> (ø)` | |
| [vm/go\_map.go](https://codecov.io/gh/goby-lang/goby/pull/866/diff?src=pr&el=tree#diff-dm0vZ29fbWFwLmdv) | `87.50% <0.00%> (ø)` | |
| [vm/call\_object.go](https://codecov.io/gh/goby-lang/goby/pull/866/diff?src=pr&el=tree#diff-dm0vY2FsbF9vYmplY3QuZ28=) | `91.22% <0.00%> (ø)` | |
| [vm/block.go](https://codecov.io/gh/goby-lang/goby/pull/866/diff?src=pr&el=tree#diff-dm0vYmxvY2suZ28=) | `63.33% <0.00%> (ø)` | |
| [vm/regexp.go](https://codecov.io/gh/goby-lang/goby/pull/866/diff?src=pr&el=tree#diff-dm0vcmVnZXhwLmdv) | `78.04% <0.00%> (ø)` | |
| [native/ripper/ripper.go](https://codecov.io/gh/goby-lang/goby/pull/866/diff?src=pr&el=tree#diff-bmF0aXZlL3JpcHBlci9yaXBwZXIuZ28=) | `70.37% <0.00%> (ø)` | |
| [native/db/db.go](https://codecov.io/gh/goby-lang/goby/pull/866/diff?src=pr&el=tree#diff-bmF0aXZlL2RiL2RiLmdv) | `56.81% <0.00%> (ø)` | |
| ... and [45 more](https://codecov.io/gh/goby-lang/goby/pull/866/diff?src=pr&el=tree-more) | |

------

[Continue to review full report at Codecov](https://codecov.io/gh/goby-lang/goby/pull/866?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/goby-lang/goby/pull/866?src=pr&el=footer). Last update [1dd1905...c9afd5d](https://codecov.io/gh/goby-lang/goby/pull/866?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).


Patch:
diff --git a/vm/http.go b/vm/http.go
index 6b65007..0201288 100644
--- a/vm/http.go
+++ b/vm/http.go
@@ -38,6 +38,9 @@ var builtinHTTPClassMethods = []*BuiltinMethodObject{
 			}
 
 			uri, err := url.Parse(arg0.value)
+			if err != nil {
+				return t.vm.InitErrorObject(errors.HTTPError, sourceLine, couldNotCompleteRequest, err)
+			}
 
 			if len(args) > 1 {
 				var arr []string
@@ -119,50 +122,19 @@ var builtinHTTPClassMethods = []*BuiltinMethodObject{
 		// Sends a HEAD request to the target with type header and body. Returns the HTTP headers as a map[string]string. Will error on non-200 responses, for more control over http requests look at the `start` method.
 		Name: "head",
 		Fn: func(receiver Object, sourceLine int, t *Thread, args []Object, blockFrame *normalCallFrame) Object {
-			if len(args) < 1 {
-				return t.vm.InitErrorObject(errors.ArgumentError, sourceLine, errors.WrongNumberOfArgumentMore, 1, len(args))
-			}
-
-			arg0, ok := args[0].(*StringObject)
-			if !ok {
-				return t.vm.InitErrorObject(errors.ArgumentError, sourceLine, errors.WrongArgumentTypeFormatNum, 0, "String", args[0].Class().Name)
-			}
-
-			uri, err := url.Parse(arg0.value)
-			if err != nil {
-				return t.vm.InitErrorObject(errors.HTTPError, sourceLine, couldNotCompleteRequest, err)
-			}
-
-			if len(args) > 1 {
-				var arr []string
-
-				for i, v := range args[1:] {
-					argn, ok := v.(*StringObject)
-					if !ok {
-						return t.vm.InitErrorObject(errors.ArgumentError, sourceLine, invalidSplatArgument, v.Class().Name, i)
-					}
-					arr = append(arr, argn.value)
-				}
-
-				uri.Path = path.Join(arr...)
-			}
-
-			resp, err := http.Head(uri.String())
-			if err != nil {
-				return t.vm.InitErrorObject(errors.HTTPError, sourceLine, couldNotCompleteRequest, err)
-			}
-			if resp.StatusCode != http.StatusOK {
-				return t.vm.InitErrorObject(errors.HTTPError, sourceLine, non200Response, resp.Status, resp.StatusCode)
-			}
-
-			ret := t.vm.InitHashObject(map[string]Object{})
-
-			for k, v := range resp.Header {
-				ret.Pairs[k] = t.vm.InitStringObject(strings.Join(v, " "))
-			}
-
-			return ret
-
+			return httpMethodWithoutBody("HEAD", receiver, sourceLine, t, args, blockFrame)
+		},
+	}, {
+		// Sends a DELETE request to the target with type header and body. Returns the HTTP headers as a map[string]string. Will error on non-200 responses, for more control over http requests look at the `start` method.
+		Name: "delete",
+		Fn: func(receiver Object, sourceLine int, t *Thread, args []Object, blockFrame *normalCallFrame) Object {
+			return httpMethodWithoutBody("DELETE", receiver, sourceLine, t, args, blockFrame)
+		},
+	}, {
+		// Sends a OPTIONS request to the target with type header and body. Returns the HTTP headers as a map[string]string. Will error on non-200 responses, for more control over http requests look at the `start` method.
+		Name: "options",
+		Fn: func(receiver Object, sourceLine int, t *Thread, args []Object, blockFrame *normalCallFrame) Object {
+			return httpMethodWithoutBody("OPTIONS", receiver, sourceLine, t, args, blockFrame)
 		},
 	}, {
 		// Starts an HTTP client. This method requires a block which takes a Net::HTTP::Client object. The return value of this method is the last evaluated value of the provided block.
@@ -187,6 +159,55 @@ var builtinHTTPClassMethods = []*BuiltinMethodObject{
 }
 
 // Internal functions ===================================================
+func httpMethodWithoutBody(method string, receiver Object, sourceLine int, t *Thread, args []Object, blockFrame *normalCallFrame) Object {
+	if len(args) < 1 {
+		return t.vm.InitErrorObject(errors.ArgumentError, sourceLine, errors.WrongNumberOfArgumentMore, 1, len(args))
+	}
+
+	arg0, ok := args[0].(*StringObject)
+	if !ok {
+		return t.vm.InitErrorObject(errors.ArgumentError, sourceLine, errors.WrongArgumentTypeFormatNum, 0, "String", args[0].Class().Name)
+	}
+
+	uri, err := url.Parse(arg0.value)
+	if err != nil {
+		return t.vm.InitErrorObject(errors.HTTPError, sourceLine, couldNotCompleteRequest, err)
+	}
+
+	if len(args) > 1 {
+		var arr []string
+
+		for i, v := range args[1:] {
+			argn, ok := v.(*StringObject)
+			if !ok {
+				return t.vm.InitErrorObject(errors.ArgumentError, sourceLine, invalidSplatArgument, v.Class().Name, i)
+			}
+			arr = append(arr, argn.value)
+		}
+
+		uri.Path = path.Join(arr...)
+	}
+
+	req, err := http.NewRequest(method, uri.String(), nil)
+	if err != nil {
+		return t.vm.InitErrorObject(errors.HTTPError, sourceLine, couldNotCompleteRequest, err)
+	}
+	resp, err := http.DefaultClient.Do(req)
+	if err != nil {
+		return t.vm.InitErrorObject(errors.HTTPError, sourceLine, couldNotCompleteRequest, err)
+	}
+	if resp.StatusCode != http.StatusOK {
+		return t.vm.InitErrorObject(errors.HTTPError, sourceLine, non200Response, resp.Status, resp.StatusCode)
+	}
+
+	ret := t.vm.InitHashObject(map[string]Object{})
+
+	for k, v := range resp.Header {
+		ret.Pairs[k] = t.vm.InitStringObject(strings.Join(v, " "))
+	}
+
+	return ret
+}
 
 // Functions for initialization -----------------------------------------
 


Test patch:
diff --git a/vm/http_test.go b/vm/http_test.go
index d30c6e5..a00a907 100644
--- a/vm/http_test.go
+++ b/vm/http_test.go
@@ -36,8 +36,20 @@ func TestHTTPRequest(t *testing.T) {
 		require "net/http"
 
 		res = Net::HTTP.head("http://127.0.0.1:3000/index")
-		res["Content-Length"]
-		`, "15"},
+		res["X-Request-Method"]
+		`, "HEAD"},
+		{`
+		require "net/http"
+
+		res = Net::HTTP.delete("http://127.0.0.1:3000/index")
+		res["X-Request-Method"]
+		`, "DELETE"},
+		{`
+		require "net/http"
+
+		res = Net::HTTP.options("http://127.0.0.1:3000/index")
+		res["X-Request-Method"]
+		`, "OPTIONS"},
 	}
 
 	//block until server is ready
@@ -159,6 +171,7 @@ func startTestServer(c chan bool) {
 	m := http.NewServeMux()
 
 	m.HandleFunc("/index", func(w http.ResponseWriter, r *http.Request) {
+		w.Header().Set("X-Request-Method", r.Method)
 		w.WriteHeader(http.StatusOK)
 
 		if r.Method == http.MethodPost {
@@ -168,7 +181,7 @@ func startTestServer(c chan bool) {
 			}
 			fmt.Fprintf(w, "POST %s", b)
 		} else {
-			fmt.Fprint(w, "GET Hello World")
+			fmt.Fprintf(w, "%s Hello World", r.Method)
 		}
 
 	})