lgp

lgp

It was a much simpler exercise, but I wanted to add a few things, and well, aside from the duplication, it’s ugly.

defmodule JsonAPI do

  def query(cat,id,keys) do

    categories = %{
      "posts"    => 100,
      "comments" => 500,
      "albums"   => 100,
      "photos"   => 5000,
      "todos"    => 200,
      "users"    => 10
    }

    if cat not in Map.keys(categories) do
       {:error, ~s('#{cat}' is not a valid category.) }
     else
       if id > categories[cat] do
         {:error, ~s(The maximum id for '#{cat}' is #{categories[cat]}.) }
       else
         base = "https://jsonplaceholder.typicode.com/"
         [base, cat, "/", to_string(id)]
               |> :erlang.iolist_to_binary
               |> HTTPoison.get
               |> handle_response(keys)
       end
     end
   end

   def query(cat,id) do

     categories = %{
       "posts"    => 100,
       "comments" => 500,
       "albums"   => 100,
       "photos"   => 5000,
       "todos"     => 200,
       "users"    => 10
     }

     if cat not in Map.keys(categories) do
       {:error, ~s('#{cat}' is not a valid category.) }
     else
       if id > categories[cat] do
         {:error, ~s(The maximum id for '#{cat}' is #{categories[cat]}.) }
       else
         base = "https://jsonplaceholder.typicode.com/"
         [base, cat, "/", to_string(id)]
               |> :erlang.iolist_to_binary
               |> HTTPoison.get
               |> handle_response
       end
     end
   end

   def query(cat) do

     categories = %{
       "posts"    => 100,
       "comments" => 500,
       "albums"   => 100,
       "photos"   => 5000,
       "todos"     => 200,
       "users"    => 10
     }

     if cat not in Map.keys(categories) do
       {:error, ~s('#{cat}' is not a valid category.) }
     else
         base = "https://jsonplaceholder.typicode.com/"
         [base, cat]
               |> :erlang.iolist_to_binary
               |> HTTPoison.get
               |> handle_response
     end
   end

   def handle_response( {:ok, %{status_code: 200, body: body} = _response}, keys ) do
     target = body
            |> Poison.Parser.parse!(%{})
            |> get_in(keys)

     {:ok, target}
   end

   def handle_response( {:ok, %{status_code: status, body: body} = _response}, _keys) do
     message = body
               |> Poison.Parser.parse!(%{})
               |> get_in(["message"])
     {:error, status, message }
   end

   def handle_response( {:error, reason }, _ ) do
     {:error, reason}
   end

   def handle_response( {:ok, %{status_code: 200, body: body} = _response}) do
     target = body
            |> Poison.Parser.parse!(%{})

     {:ok, target}
   end

   def handle_response( {:ok, %{status_code: status, body: body} = _response}) do
     message = body
               |> Poison.Parser.parse!(%{})
               |> get_in(["message"])
     {:error, status, message }
   end

   def handle_response( {:error, reason }) do
     {:error, reason}
   end
 end

Showing Posts 1 to 10

kartheek

kartheek

Hi @lgp can you add new line with ``` at the beginning of the code block and end of code blocks. You can edit post and add them:

```
Your Code
```

Also your question is not clear as to what you want to cleanup ? Can you provide more details.

kodepett

kodepett

You can convert the nested if/else to functions.

lgp

lgp OP

OK. How do I edit the post? I haven’t found a way.

al2o3cr

al2o3cr

@lgp IIRC there’s an edit timeout for regular users or something, I went ahead and added backticks to your post.

Some general thoughts on cleaning up the code:

  • chains of if / else may be more readable as cond
  • a common idiom with functions with optional arguments is that the shorter versions (like query/1 and query/2 above) fill in the remaining arguments and call the “longer” version (query/3 here)
  • consider extracting common stanzas (like the lines that start with base = "https://jsonplaceholder.typicode.com/") to a private function instead of repeating them
lgp

lgp OP

Thanks very much. I got rid of the nested ifs.

There really are no default arguments for the query/1 and query/2 versions. I would have to put so many conditionals inside the main function that I would be no better off. I’ll keep looking at that, though.

I’ll also look at using a private function for the URL building lines. Not sure how much that would save since handling the various options would introduce a lot of complications I think.

Again, thanks for the reply – and for adding the back ticks!

  • Larry
stevensonmt

stevensonmt

which course is this from?

lgp

lgp OP

The Pragmatic Studio “Elixir/OTP” course. This exercise was from the notes, not one of the videos. And as I mentioned, I expanded on it a bit.

lgp

lgp OP

OK. Took care of the other suggestions.
larry@habu lib % ll json*
-rw-r–r–@ 1 larry staff 2787 Mar 5 17:57 json_api.ex
-rw-r–r–@ 1 larry staff 2003 Mar 5 17:57 json_api.new.ex
larry@habu lib % wc json*
112 301 2787 json_api.ex
90 213 2003 json_api.new.ex
202 514 4790 total
larry@habu lib %
I’ll post the new version for comparison.

lgp

lgp OP

defmodule JsonAPI do
  def query(cat, id \\ 0, keys \\ []) do
    categories = %{
      "posts"    => 100,
      "comments" => 500,
      "albums"   => 100,
      "photos"   => 5000,
      "todos"    => 200,
      "users"    => 10
    }

    cond do
      cat not in Map.keys(categories) ->
        {:error, ~s('#{cat}' is not a valid category.)}

      id > categories[cat] ->
        {:error, ~s(The maximum id for '#{cat}' is #{categories[cat]}.)}

      true ->
        get_url(cat, id, keys)
    end
  end

  def handle_response({:ok, %{status_code: 200, body: body} = _response}, keys) do
    target =
      body
      |> Poison.Parser.parse!(%{})
      |> get_in(keys)

    {:ok, target}
  end

  def handle_response({:ok, %{status_code: status, body: body} = _response}, _keys) do
    message =
      body
      |> Poison.Parser.parse!(%{})
      |> get_in(["message"])

    {:error, status, message}
  end

  def handle_response({:error, reason}, _) do
    {:error, reason}
  end

  def handle_response({:ok, %{status_code: 200, body: body} = _response}) do
    target =
      body
      |> Poison.Parser.parse!(%{})

    {:ok, target}
  end

  def handle_response({:ok, %{status_code: status, body: body} = _response}) do
    message =
      body
      |> Poison.Parser.parse!(%{})
      |> get_in(["message"])

    {:error, status, message}
  end

  def handle_response({:error, reason}) do
    {:error, reason}
  end

  defp get_url(cat, id, keys) do
    base = "https://jsonplaceholder.typicode.com/"

    cond do
      is_list(keys) and length(keys) > 0 ->
        [base, cat, "/", to_string(id)]
        |> :erlang.iolist_to_binary()
        |> HTTPoison.get()
        |> handle_response(keys)

      id > 0 ->
        [base, cat, "/", to_string(id)]
        |> :erlang.iolist_to_binary()
        |> HTTPoison.get()
        |> handle_response

      true ->
        [base, cat]
        |> :erlang.iolist_to_binary()
        |> HTTPoison.get()
        |> handle_response
    end
  end
end

Still need to work on the duplication in handle_response, but this is a great improvement. Thanks again!

lgp

lgp OP

And now cleaned up handle_response. Overall quite an improvement:
larry@habu lib % wc json*
113 304 2873 json_api.ex
73 185 1669 json_api.fin.ex
186 489 4542 total
larry@lil-habu lib %

And the (for now) final version:

defmodule JsonAPI do
  def query(cat, id \\ 0, keys \\ []) do
    categories = %{
      "posts"    => 100,
      "comments" => 500,
      "albums"   => 100,
      "photos"   => 5000,
      "todos"    => 200,
      "users"    => 10
    }

    cond do
      cat not in Map.keys(categories) ->
        {:error, ~s('#{cat}' is not a valid category.)}

      id > categories[cat] ->
        {:error, ~s(The maximum id for '#{cat}' is #{categories[cat]}.)}

      true ->
        get_url(cat, id, keys)
    end
  end

  def handle_response( response, keys \\ [])

  def handle_response({:ok, %{status_code: 200, body: body} = _response}, keys) do
    target =
      body
      |> Poison.Parser.parse!(%{})
    if length(keys) > 0 do
      {:ok, target |> get_in(keys) }
    else
      {:ok, target}
    end
  end

  def handle_response({:ok, %{status_code: status, body: body} = _response}, _keys) do
    message =
      body
      |> Poison.Parser.parse!(%{})
      |> get_in(["message"])

    {:error, status, message}
  end

  def handle_response({:error, reason}, _) do
    {:error, reason}
  end

  defp get_url(cat, id, keys) do
    base = "https://jsonplaceholder.typicode.com/"

    cond do
      is_list(keys) and length(keys) > 0 ->
        [base, cat, "/", to_string(id)]
        |> :erlang.iolist_to_binary()
        |> HTTPoison.get()
        |> handle_response(keys)

      id > 0 ->
        [base, cat, "/", to_string(id)]
        |> :erlang.iolist_to_binary()
        |> HTTPoison.get()
        |> handle_response

      true ->
        [base, cat]
        |> :erlang.iolist_to_binary()
        |> HTTPoison.get()
        |> handle_response
    end
  end
end

Thanks once more…

Where Next? Top

Trending in Questions Top

RSP87
I’m working on a project that simulates the bumbl example in the programming phoenix book. It acts almost like an email client. We have a...
New
kszambelanczyk
Hello! Could someone please give me a help/sample code, how to delete a file from s3 using waffle/waffle_ecto from Phoenix app. I creat...
New
RemyXRenard
I’m seeing that a list inside a Kino.DataTable will be interpreted as a charlist, even if the Kino.configure() is set to charlists: :as_l...
New
velrest
So my question is quite simple and i have found no conclusive answer on forum, google or AI. Should we use :erlang.float for Integer to ...
New
samoloth
Hi, I’ve just set up an application with ash_authentication. There is only magic link strategy for now, so there is no confirmation add o...
New
FlyingNoodle
If a change or preparation module uses Ash.Changeset.get_argument/2 or Ash.Query.get_argument/2 (or any of the other get_argument functio...
New
nseaSeb
Hello, I know there is an approach for handling lists that allows for optimized traversal, but I can’t recall the specific method (somet...
New

Other Trending Topics Top

mudasobwa
I am happy to introduce the very α version of the new programming language compiled to BEAM. Welcome Cure. It has literally three kille...
New
marciok
Hi there! We created Gust: A task orchestrator inspired by Airflow. For those who have never heard about Aiflow, it’s a Python-based wor...
New
jimsynz
Beam Bots (or just BB for short) is a framework for building fault-tolerant robotics applications in Elixir using familiar OTP patterns. ...
New
Dmk
Xamal is a deployment tool for Elixir apps that deploys native releases to bare metal servers over SSH. It’s a port of GitHub - basecamp/...
New
Damirados
Hello everyone. After busy few months I am happy to announce v0.1.0 of Emerge & Solve. They are GUI (Emerge) and State management (S...
New
netoum
Corex is an accessible, unstyled UI component library for Phoenix that integrates Zag.js state machines using Vanilla JavaScript and Live...
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews