Skip to content

Commit

Permalink
Add bang methods (#83)
Browse files Browse the repository at this point in the history
* add bang functions for HTTP requests.  return http error response struct by default when there is an issue.  add catch all for success? when params are not expected

* add other bang function tests

* update doc strings

* refactor bang functions to use #request!.

* clean up code to keep dry
  • Loading branch information
bradleyd authored and valpackett committed May 6, 2016
1 parent 98130b4 commit c2ed54f
Show file tree
Hide file tree
Showing 4 changed files with 71 additions and 8 deletions.
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,12 @@ iex> HTTPotion.get "http://ip6.me", [ ibrowse: [ proxy_host: 'fc81:6134:ba6c:845
# The default timeout is 5000 ms, but can be changed
iex> HTTPotion.get "http://example.com", [timeout: 10_000]

# If there is an error a `HTTPotion.ErrorResponse` is returned
iex> HTTPotion.get "http://localhost:1"
%HTTPotion.ErrorResponse{message: "econnrefused"}

# You can also raise `HTTPError` with the `bang` version of request
iex> HTTPotion.get! "http://localhost:1"
** (HTTPotion.HTTPError) econnrefused
```

Expand Down
53 changes: 48 additions & 5 deletions lib/httpotion.ex
Original file line number Diff line number Diff line change
Expand Up @@ -165,9 +165,9 @@ defmodule HTTPotion.Base do
* `follow_redirects` - if true and a response is a redirect, header[:Location] is taken for the next request
Returns `HTTPotion.Response` or `HTTPotion.AsyncResponse` if successful.
Raises `HTTPotion.HTTPError` if failed.
Returns `HTTPotion.ErrorResponse` if failed.
"""
@spec request(atom, String.t, [{atom(), any()}]) :: %HTTPotion.Response{} | %HTTPotion.AsyncResponse{}
@spec request(atom, String.t, [{atom(), any()}]) :: %HTTPotion.Response{} | %HTTPotion.AsyncResponse{} | %HTTPotion.ErrorResponse{}
def request(method, url, options \\ []) do
args = process_arguments(method, url, options)
response = if conn_pid = Keyword.get(options, :direct) do
Expand All @@ -185,6 +185,18 @@ defmodule HTTPotion.Base do
end
end

@doc """
Like `request`, but raises `HTTPotion.HTTPError` if failed.
"""
@spec request!(atom, String.t, [{atom(), any()}]) :: %HTTPotion.Response{} | %HTTPotion.AsyncResponse{}
def request!(method, url, options \\ []) do
case request(method, url, options) do
%HTTPotion.ErrorResponse{message: message} ->
raise HTTPotion.HTTPError, message: message
response -> response
end
end

defp normalize_location(location, url) do
if String.starts_with?(location, "http") do
location
Expand Down Expand Up @@ -224,28 +236,48 @@ defmodule HTTPotion.Base do
{ :ibrowse_req_id, id } ->
%HTTPotion.AsyncResponse{ id: id }
{ :error, { :conn_failed, { :error, reason }}} ->
raise HTTPotion.HTTPError, message: error_to_string(reason)
%HTTPotion.ErrorResponse{ message: error_to_string(reason)}
{ :error, :conn_failed } ->
raise HTTPotion.HTTPError, message: "conn_failed"
%HTTPotion.ErrorResponse{ message: "conn_failed"}
{ :error, reason } ->
raise HTTPotion.HTTPError, message: error_to_string(reason)
%HTTPotion.ErrorResponse{ message: error_to_string(reason)}
end
end

@doc "A shortcut for `request(:get, url, options)`."
def get(url, options \\ []), do: request(:get, url, options)
@doc "A shortcut for `request!(:get, url, options)`."
def get!(url, options \\ []), do: request!(:get, url, options)

@doc "A shortcut for `request(:put, url, options)`."
def put(url, options \\ []), do: request(:put, url, options)
@doc "A shortcut for `request!(:put, url, options)`."
def put!(url, options \\ []), do: request!(:put, url, options)

@doc "A shortcut for `request(:head, url, options)`."
def head(url, options \\ []), do: request(:head, url, options)
@doc "A shortcut for `request!(:head, url, options)`."
def head!(url, options \\ []), do: request!(:head, url, options)

@doc "A shortcut for `request(:post, url, options)`."
def post(url, options \\ []), do: request(:post, url, options)
@doc "A shortcut for `request!(:post, url, options)`."
def post!(url, options \\ []), do: request!(:post, url, options)

@doc "A shortcut for `request(:patch, url, options)`."
def patch(url, options \\ []), do: request(:patch, url, options)
@doc "A shortcut for `request!(:patch, url, options)`."
def patch!(url, options \\ []), do: request!(:patch, url, options)

@doc "A shortcut for `request(:delete, url, options)`."
def delete(url, options \\ []), do: request(:delete, url, options)
@doc "A shortcut for `request!(:delete, url, options)`."
def delete!(url, options \\ []), do: request!(:delete, url, options)

@doc "A shortcut for `request(:options, url, options)`."
def options(url, options \\ []), do: request(:options, url, options)
@doc "A shortcut for `request!(:options, url, options)`."
def options!(url, options \\ []), do: request!(:options, url, options)

defoverridable Module.definitions_in(__MODULE__)
end
Expand All @@ -267,10 +299,21 @@ defmodule HTTPotion do
def success?(%__MODULE__{ status_code: code }) do
code in 200..299
end
def success?(_unknown) do
false
end

def success?(%__MODULE__{ status_code: code } = response, :extra) do
success?(response) or code in [302, 304]
end
def success?(_unknown, _extra) do
false
end

end

defmodule ErrorResponse do
defstruct message: nil
end

defmodule Headers do
Expand Down
2 changes: 1 addition & 1 deletion test/direct_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ defmodule DirectTest do
test "exception" do
assert_raise HTTPotion.HTTPError, "econnrefused", fn ->
{:ok, pid} = HTTPotion.spawn_worker_process("localhost:1")
IO.puts HTTPotion.get("localhost:1/lolwat", [direct: pid])
IO.puts HTTPotion.get!("localhost:1/lolwat", [direct: pid])
end
end

Expand Down
19 changes: 17 additions & 2 deletions test/httpotion_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -80,9 +80,24 @@ defmodule HTTPotionTest do
assert HTTPotion.process_url("http://example.com", [query: %{param: "value"}]) == "http://example.com?param=value"
end

test "exception" do
test "get exception" do
assert_raise HTTPotion.HTTPError, "econnrefused", fn ->
HTTPotion.get("localhost:1")
HTTPotion.get!("localhost:1")
end
end
test "put exception" do
assert_raise HTTPotion.HTTPError, "econnrefused", fn ->
HTTPotion.put!("localhost:1")
end
end
test "delete exception" do
assert_raise HTTPotion.HTTPError, "econnrefused", fn ->
HTTPotion.delete!("localhost:1")
end
end
test "post exception" do
assert_raise HTTPotion.HTTPError, "econnrefused", fn ->
HTTPotion.post!("localhost:1")
end
end

Expand Down

0 comments on commit c2ed54f

Please sign in to comment.