Installed Elixir---> NOW WHAT?

Sat Feb 9 19:44:11 2019

Load up you App in REPL(iex)

So I have created a new app. Easy peezy.

mix new podder --sup 

Although TDD is proposed , I like to play around a bit with what I am building in the REPL Loading the App created in REPL like this

iex -S mix

This loads the Application in the REPL. We can reload with command

recompile

What will we create

We will create a PodCast app that gets the data from Various Podcasts. We use Genserver per Podcast name. It will fetch data from the API and then store data fetched in ETS for each Podcast.

GenServer

Genserver starts an Elixir Process and holds its own state. It has a set of callbacks which can be used to Apply the business Logic.

We start a Genserver per Podcast. I want to name each Genserver as I dont want to deal with PIDs. For that I use the :global atom to store the name in the Global Module.

  def start_link(state, name) do
    GenServer.start_link(__MODULE__, state, name: {:global, name})
  end

From iex start the Genserver as:

iex(1)> Podder.start_link(%{}, "jabber")
{:ok, #PID<0.134.0>}
iex(2)> Podder.get_episodes({:global, "jabber"})
%{}
iex(3)> 

The above starts a GenServer with name jabber which is registered in the global Registry

Observer

Fire up an Elixir Shell and

:observer.start 

Fault Tolerance

Fault Tolerance is baked into the Elixir/Erlang Systems and we will achieve it with Supervision Trees

So our app will start a Supervision Tree , which will start a Dynamic Supervisor We need a Dynamic Supervisor as we dont know how many PodCast GenServer we will be starting when the App Starts. As and when I add my Favorite PodCast, the Dynamic Supervisor starts a new Podcast Genserver.

More About GenServers

Lets look a little bit more about GenServers

According to Docs

A GenServer is a process like any other Elixir process and it can be used to keep state, execute code asynchronously and so on.

So the basic structure of our App is as Follows

We have multiple users who will register into the App and subscribe to their Favorite Podcasts. For each user we spin up a Genserver using Dynamic Supervisor as mentioned above. Each GenServer holds some State, like the Ids of the Podcasts I am Subscribed to. It also provides us an interface to search podcasts using wildcard search.

Note: We use a third party API to get our Podcast Data

Local Registry

All the GenServers we spin up for each user returns us a PID, with which we can interact with the GenServer. Remembering and storing PIDs is not something I would like to do, hence we use the Local Process Registry to give the GenServer a name and basically get the internal pid of the GenServer using the unique name.

Basically when starting the GenServer we start it with the via module

GenServer.start_link(__MODULE__, state, name: via_tuple(user_name)) 

Note the via_tuple it takes the Name and stores it in the Registry

defp via_tuple(name), do: {:via, Registry, {Podder.Registry, name}}

We can get the PID of the process as below

def whereis(name: name), do: Registry.whereis_name({Podder.Registry, name})

We also need to start the Registry, so in our main Application we start the Registry under a Supervisor

  def start(_type, _args) do
    # List all child processes to be supervised
    children = [
      # Starts a worker by calling: Podder.Worker.start_link(arg)
      # {Podder.Worker, arg},

      %{
        id: Registry,
        start: {Registry, :start_link, [[keys: :unique, name: Podder.Registry]]}
      },
      %{
        id: Podder.DynamicSupervisor,
        start: {Podder.DynamicSupervisor, :start_link, []}
      }
    ]

    # See https://hexdocs.pm/elixir/Supervisor.html
    # for other strategies and supported options
    opts = [strategy: :one_for_one]
    Supervisor.start_link(children, opts)
  end
	```
The above code starts a Local Process registry and a Dynamic Supervisor when our
Application starts.


###### Genserver callbacks

###### handle_call

When our server needs to return something back to the caller we use the
handle_call.
For our case we use it search for Podcasts and return the search results, we
dont have to modify the state, so we can write like.


```elixir
  def handle_call({:search, query}, from, state) do
    response =
      with {:ok, result} <- Podder.ListenProvider.API.search_podcasts(query) do
        result
        |> Map.get("podcasts")
        |> Enum.map(fn x -> Map.take(x, @fields_podcasts) end)
      else
        _ -> %{}
      end

    {:reply, response, state}
  end
See the return from the function, we query the API( I had used Task.Async to
query the API before, but thats an overkill)

Return the response and keep the same state as before. This completes the
Query interface

*Note: Our Genserver will be blocked till the Query from the external API
returns*

####### handle_cast

We will use this only to change the state and not return any response back to
the caller
	  def handle_cast({:save, items}, state) do
    my_items =
      cond do
        is_list(items) -> items
        true -> [items]
      end

    new_pods = Map.get(state, "podcasts") |> Enum.concat(my_items)
    new_state = Map.put(state, "podcasts", new_pods)
    {:noreply, new_state}
   end
Here we take an item , item is podcast or multiple podcasts that the user has
subscribed.
We change our state with new Podcasts the user has subscribed. Note there is
nothing returned back to the calling process and our Genserver is not blocked.

####### handle_info

Usually we can run periodic tasks here. We can see later about this.
Written on February 9, 2019