I'm assuming you mean the GenServer part?
What I had was my main app/root supervisor in Application.ex:
Code:
defmodule MyApp.Application do
use Application
def start(_type, _args) do
import Supervisor.Spec
children = [
#...usual stuff
supervisor(MyApp.Social.Supervisor, []) # supervisor for all my social media GenServers
]
opts = [strategy: :one_for_one, name: MyApp.Supervisor] # the usual defaults.
Supervisor.start_link(children, opts)
end
end
Supervisor I started in the app's root supervisor for social media:
Code:
defmodule MyApp.Social.Supervisor do
use Supervisor
############ Supervisor API##############
def start_link do
Supervisor.start_link(__MODULE__,[], name: __MODULE__) #This gets called from the Application.ex. This pretty much activates this Supervisor and will restart it on error.
end
def init(_) do
children = [
worker(MyApp.Social.Twitter, [], id: "tweets") #This calls the start_link function in the child GenServer.
]
supervise(children, strategy: :one_for_one) # again, practically defaults. but :simple_one_for_one maybe when you want to dynamically add workers.
end
The code to run twitter calls, psuedo-code:
Code:
defmodule MyApp.Social.Twitter do
use GenServer
##### Public API - The functions other modules can call####
def start_link do
GenServer.start_link(__MODULE__, [], name: __MODULE__)
end
def pull_tweets(_args) do
GenServer.call(__MODULE__, :pull_tweets)
end
##### GenServer API - these functions are called based on the Public API ####
# init is automatically ran after start_link is called.
def init(_state) do
token = get_twitter_token() #some function I have for authentication.
tweets = get_new_tweets(token)
reschedule() ####this does the scheduling, we're just setting it on init.
{:ok, %{token: token, tweets: tweets}}
end
#the public function above pull_tweets sends the :pull_tweets message, this function pattern matches on it:
def handle_call(:pull_tweets, _from, state) do
tweets = state.tweets
{:reply, tweets, state} # tweets is the reply, state isn't being changed.
end
# sets new tweets every 30 minutes - pattern matches from message send by reschedule()
def handle_info(:reschedule, state) do
tweets = get_new_tweets(state.token)
reschedule() # Reschedule once more
{:noreply, %{token: state.token, stories: stories}}
end
### Helper functions
defp get_new_tweets(token) do
tweets = make_query_urls
|> make_tweet_requests(token) #starts async requests with Task.async on a list of query strings.
|> Enum.map(&(parse_function(&1) )) # runs Task.await to get responses from each Task.async and decodes the json.
tweets
end
defp make_query_urls() do
#...builds url query strings ...
return [list of query strings to search]
end
defp make_tweet_requests(query_urls, token) do
tasks =
Enum.map(query_urls, fn query_url ->
Task.async(fn -> HTTPoison.get(query_url, headers) end)
end)
# tasks is a list of requests started asynchronously.
tasks
end
#schedule set to get new tweets every 30 minutes
defp reschedule() do
Process.send_after(self(), :reschedule, 1 * 60 * 30000)
end
end
Pretty much the GenServer makes async requests when the app starts and stores the results, then fetches tweets asynchronously again every 30 minutes and stores the results again.
Then to actually get the tweets, other modules just call MyApp.Social.Twitter.pull_tweets which is the public function on the GenServer. Only two functions in that GenServer are exposed: start_link and pull_tweets, but only the app itself calls start_link.
The social media supervisor is responsible for Facebook, Twitter, Instagram etc. GenServers (Twitter's is shown above). But that one supervisor is in charge of them all - which I may have to change the restart strategy for.
Pretty much each genserver is a separate process that spawns other processes with Task to make async requests. It's pretty much one big ass helper function that stores state.
If you have any questions, just let me know, I've spent months with the language.