Skip to content

Commit

Permalink
Update Deprecated :simple_one_to_one to use DynamicSupervisor in Elix…
Browse files Browse the repository at this point in the history
…ir 1.6 (#89)

* Changes dynamic supervision with DynamicSupervisor. Add ExIRC.start_link\/1

* Add a GitHub action for automated testing (#90)

* Changes dynamic supervision with DynamicSupervisor. Add ExIRC.start_link\/1

* Removing ExIRC.start_link!/1. Adding ExIRC.TemporaryClient to define temporary restart children specs for dynamic supervision.

* Adding specs to the ExIRC module.

* Update documentation and examples for new child module specs

* Updating Supervisor documentation link

* Add tests badge to README.md

* Updating travis ci to use elixir 1.6

* Update github tests badge to use correct repo

* Update travis ci erlang OTP to version 20.3

* Test: start a client linked to the caller

Co-authored-by: Théophile Choutri <theophile@choutri.eu>
  • Loading branch information
rockerBOO and tchoutri authored Jul 19, 2020
1 parent f4e5804 commit 4b35942
Show file tree
Hide file tree
Showing 8 changed files with 131 additions and 80 deletions.
6 changes: 2 additions & 4 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
language: elixir
sudo: false
elixir:
- 1.3
- 1.4
- 1.6
otp_release:
- 18.3
- 19.1
- 20.3
script:
- "MIX_ENV=test mix do deps.get, compile, coveralls.travis"
notifications:
Expand Down
12 changes: 6 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# ExIRC

[![Build Status](https://travis-ci.org/bitwalker/exirc.svg?branch=master)](https://travis-ci.org/bitwalker/exirc)
![.github/workflows/tests.yaml](https://github.com/bitwalker/exirc/workflows/.github/workflows/tests.yaml/badge.svg)
[![Hex.pm Version](http://img.shields.io/hexpm/v/exirc.svg?style=flat)](https://hex.pm/packages/exirc)

ExIRC is a IRC client library for Elixir projects. It aims to have a clear, well
Expand Down Expand Up @@ -83,21 +84,20 @@ on until it attempts to join a channel. Please see the `examples` directory for
defmodule ExampleApplication do
use Application

# See http://elixir-lang.org/docs/stable/elixir/Application.html
# See https://hexdocs.pm/elixir/Application.html
# for more information on OTP Applications
@impl true
def start(_type, _args) do
import Supervisor.Spec, warn: false

{:ok, client} = ExIRC.start_link!

children = [
# Define workers and child supervisors to be supervised
worker(ExampleConnectionHandler, [client]),
{ExampleConnectionHandler, [client]},
# here's where we specify the channels to join:
worker(ExampleLoginHandler, [client, ["#ohaibot-testing"]])
{ExampleLoginHandler, [client, ["#ohaibot-testing"]]}
]

# See http://elixir-lang.org/docs/stable/elixir/Supervisor.html
# See https://hexdocs.pm/elixir/Supervisor.html
# for other strategies and supported options
opts = [strategy: :one_for_one, name: ExampleApplication.Supervisor]
Supervisor.start_link(children, opts)
Expand Down
12 changes: 6 additions & 6 deletions examples/bot/lib/example.ex
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@ defmodule Example do

alias Example.Bot

# See http://elixir-lang.org/docs/stable/elixir/Application.html
# See https://hexdocs.pm/elixir/Application.html
# for more information on OTP Applications
@impl true
def start(_type, _args) do
import Supervisor.Spec, warn: false
children =
Application.get_env(:exirc_example, :bots)
|> Enum.map(fn bot -> worker(Bot, [bot]) end)

children = Application.get_env(:exirc_example, :bots)
|> Enum.map(fn bot -> worker(Bot, [bot]) end)

# See http://elixir-lang.org/docs/stable/elixir/Supervisor.html
# See https://hexdocs.pm/elixir/Supervisor.html
# for other strategies and supported options
opts = [strategy: :one_for_one, name: Example.Supervisor]
Supervisor.start_link(children, opts)
Expand Down
17 changes: 9 additions & 8 deletions examples/bot/mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,21 @@ defmodule Example.Mixfile do
use Mix.Project

def project do
[app: :exirc_example,
version: "0.0.1",
elixir: "~> 1.2",
build_embedded: Mix.env == :prod,
start_permanent: Mix.env == :prod,
deps: deps]
[
app: :exirc_example,
version: "0.0.1",
elixir: "~> 1.6",
build_embedded: Mix.env() == :prod,
start_permanent: Mix.env() == :prod,
deps: deps()
]
end

# Configuration for the OTP application
#
# Type "mix help compile.app" for more information
def application do
[applications: [:logger, :exirc],
mod: {Example, []}]
[applications: [:logger, :exirc], mod: {Example, []}]
end

# Dependencies can be Hex packages:
Expand Down
42 changes: 27 additions & 15 deletions lib/exirc/exirc.ex
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,24 @@ defmodule ExIRC do
ExIRC.Client.stop! client
"""
use Supervisor
import Supervisor.Spec
use DynamicSupervisor

defmodule TemporaryClient do
@moduledoc """
Temporary ExIRC.Client.
"""

@doc """
Defines how this module will run as a child process.
"""
def child_spec(arg) do
%{
id: __MODULE__,
start: {ExIRC.Client, :start_link, [arg]},
restart: :temporary
}
end
end

##############
# Public API
Expand All @@ -39,37 +55,33 @@ defmodule ExIRC do
@doc """
Start the ExIRC supervisor.
"""
@spec start! :: {:ok, pid} | {:error, term}
@spec start!() :: Supervisor.on_start()
def start! do
Supervisor.start_link(__MODULE__, [], name: :exirc)
DynamicSupervisor.start_link(__MODULE__, [], name: :exirc)
end

@doc """
Start a new ExIRC client under the ExIRC supervisor
"""
@spec start_client! :: {:ok, pid} | {:error, term}
def start_client! do
# Start the client worker
Supervisor.start_child(:exirc, [[owner: self()]])
@spec start_client!() :: DynamicSupervisor.on_start_child()
def start_client!() do
DynamicSupervisor.start_child(:exirc, {TemporaryClient, owner: self()})
end

@doc """
Start a new ExIRC client
"""
@spec start_link!() :: GenServer.on_start()
def start_link! do
ExIRC.Client.start!([owner: self()])
ExIRC.Client.start!(owner: self())
end

##############
# Supervisor API
##############

@spec init(any) :: {:ok, pid} | {:error, term}
@impl true
def init(_) do
children = [
worker(ExIRC.Client, [], restart: :temporary)
]
supervise children, strategy: :simple_one_for_one
DynamicSupervisor.init(strategy: :one_for_one)
end

end
31 changes: 20 additions & 11 deletions mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -2,35 +2,44 @@ defmodule ExIRC.Mixfile do
use Mix.Project

def project do
[app: :exirc,
[
app: :exirc,
version: "1.1.0",
elixir: "~> 1.0",
elixir: "~> 1.6",
description: "An IRC client library for Elixir.",
package: package(),
test_coverage: [tool: ExCoveralls],
preferred_cli_env: [coveralls: :test, "coveralls.detail": :test, "coveralls.html": :test, "coveralls.post": :test],
deps: deps()]
preferred_cli_env: [
coveralls: :test,
"coveralls.detail": :test,
"coveralls.html": :test,
"coveralls.post": :test
],
deps: deps()
]
end

# Configuration for the OTP application
def application do
[mod: {ExIRC.App, []},
applications: [:ssl, :crypto, :inets]]
[mod: {ExIRC.App, []}, applications: [:ssl, :crypto, :inets]]
end

defp package do
[ files: ["lib", "mix.exs", "README.md", "LICENSE"],
[
files: ["lib", "mix.exs", "README.md", "LICENSE"],
maintainers: ["Paul Schoenfelder"],
licenses: ["MIT"],
links: %{ "GitHub" => "https://github.com/bitwalker/exirc",
"Home Page" => "http://bitwalker.org/exirc"} ]
links: %{
"GitHub" => "https://github.com/bitwalker/exirc",
"Home Page" => "http://bitwalker.org/exirc"
}
]
end

defp deps do
[
{:ex_doc, "~> 0.19", only: :dev},
{:excoveralls, "~> 0.10", only: :test},
{:excoveralls, "~> 0.10", only: :test}
]
end

end
30 changes: 15 additions & 15 deletions mix.lock
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
%{
"certifi": {:hex, :certifi, "2.4.2", "75424ff0f3baaccfd34b1214184b6ef616d89e420b258bb0a5ea7d7bc628f7f0", [:rebar3], [{:parse_trans, "~>3.3", [hex: :parse_trans, repo: "hexpm", optional: false]}], "hexpm"},
"earmark": {:hex, :earmark, "1.3.1", "73812f447f7a42358d3ba79283cfa3075a7580a3a2ed457616d6517ac3738cb9", [:mix], [], "hexpm"},
"ex_doc": {:hex, :ex_doc, "0.19.2", "6f4081ccd9ed081b6dc0bd5af97a41e87f5554de469e7d76025fba535180565f", [:mix], [{:earmark, "~> 1.2", [hex: :earmark, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.10", [hex: :makeup_elixir, repo: "hexpm", optional: false]}], "hexpm"},
"excoveralls": {:hex, :excoveralls, "0.10.4", "b86230f0978bbc630c139af5066af7cd74fd16536f71bc047d1037091f9f63a9", [:mix], [{:hackney, "~> 1.13", [hex: :hackney, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm"},
"certifi": {:hex, :certifi, "2.4.2", "75424ff0f3baaccfd34b1214184b6ef616d89e420b258bb0a5ea7d7bc628f7f0", [:rebar3], [{:parse_trans, "~>3.3", [hex: :parse_trans, repo: "hexpm", optional: false]}], "hexpm", "01d479edba0569a7b7a2c8bf923feeb6dc6a358edc2965ef69aea9ba288bb243"},
"earmark": {:hex, :earmark, "1.3.1", "73812f447f7a42358d3ba79283cfa3075a7580a3a2ed457616d6517ac3738cb9", [:mix], [], "hexpm", "000aaeff08919e95e7aea13e4af7b2b9734577b3e6a7c50ee31ee88cab6ec4fb"},
"ex_doc": {:hex, :ex_doc, "0.19.2", "6f4081ccd9ed081b6dc0bd5af97a41e87f5554de469e7d76025fba535180565f", [:mix], [{:earmark, "~> 1.2", [hex: :earmark, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.10", [hex: :makeup_elixir, repo: "hexpm", optional: false]}], "hexpm", "4eae888633d2937e0a8839ae6002536d459c22976743c9dc98dd05941a06c016"},
"excoveralls": {:hex, :excoveralls, "0.10.4", "b86230f0978bbc630c139af5066af7cd74fd16536f71bc047d1037091f9f63a9", [:mix], [{:hackney, "~> 1.13", [hex: :hackney, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "1ed31bd0cae718b50d25c3de625d32aacc974553fa7a4f340cc60a2f10aee525"},
"exjsx": {:hex, :exjsx, "3.2.1", "1bc5bf1e4fd249104178f0885030bcd75a4526f4d2a1e976f4b428d347614f0f", [:mix], [{:jsx, "~> 2.8.0", [hex: :jsx, optional: false]}]},
"hackney": {:hex, :hackney, "1.15.0", "287a5d2304d516f63e56c469511c42b016423bcb167e61b611f6bad47e3ca60e", [:rebar3], [{:certifi, "2.4.2", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "6.0.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "1.0.1", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "1.0.2", [hex: :mimerl, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "1.1.4", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}], "hexpm"},
"idna": {:hex, :idna, "6.0.0", "689c46cbcdf3524c44d5f3dde8001f364cd7608a99556d8fbd8239a5798d4c10", [:rebar3], [{:unicode_util_compat, "0.4.1", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm"},
"jason": {:hex, :jason, "1.1.2", "b03dedea67a99223a2eaf9f1264ce37154564de899fd3d8b9a21b1a6fd64afe7", [:mix], [{:decimal, "~> 1.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm"},
"hackney": {:hex, :hackney, "1.15.0", "287a5d2304d516f63e56c469511c42b016423bcb167e61b611f6bad47e3ca60e", [:rebar3], [{:certifi, "2.4.2", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "6.0.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "1.0.1", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "1.0.2", [hex: :mimerl, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "1.1.4", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}], "hexpm", "b69d97134f1876ba8e4e2f405e9da8cba7cf4f2da0b7cc24a5ccef8dcf1b46b2"},
"idna": {:hex, :idna, "6.0.0", "689c46cbcdf3524c44d5f3dde8001f364cd7608a99556d8fbd8239a5798d4c10", [:rebar3], [{:unicode_util_compat, "0.4.1", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "4bdd305eb64e18b0273864920695cb18d7a2021f31a11b9c5fbcd9a253f936e2"},
"jason": {:hex, :jason, "1.1.2", "b03dedea67a99223a2eaf9f1264ce37154564de899fd3d8b9a21b1a6fd64afe7", [:mix], [{:decimal, "~> 1.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "fdf843bca858203ae1de16da2ee206f53416bbda5dc8c9e78f43243de4bc3afe"},
"jsx": {:hex, :jsx, "2.8.2", "7acc7d785b5abe8a6e9adbde926a24e481f29956dd8b4df49e3e4e7bcc92a018", [:mix, :rebar3], []},
"makeup": {:hex, :makeup, "0.8.0", "9cf32aea71c7fe0a4b2e9246c2c4978f9070257e5c9ce6d4a28ec450a839b55f", [:mix], [{:nimble_parsec, "~> 0.5.0", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm"},
"makeup_elixir": {:hex, :makeup_elixir, "0.13.0", "be7a477997dcac2e48a9d695ec730b2d22418292675c75aa2d34ba0909dcdeda", [:mix], [{:makeup, "~> 0.8", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm"},
"metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], []},
"mimerl": {:hex, :mimerl, "1.0.2", "993f9b0e084083405ed8252b99460c4f0563e41729ab42d9074fd5e52439be88", [:rebar3], []},
"nimble_parsec": {:hex, :nimble_parsec, "0.5.0", "90e2eca3d0266e5c53f8fbe0079694740b9c91b6747f2b7e3c5d21966bba8300", [:mix], [], "hexpm"},
"parse_trans": {:hex, :parse_trans, "3.3.0", "09765507a3c7590a784615cfd421d101aec25098d50b89d7aa1d66646bc571c1", [:rebar3], [], "hexpm"},
"ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.4", "f0eafff810d2041e93f915ef59899c923f4568f4585904d010387ed74988e77b", [:make, :mix, :rebar3], [], "hexpm"},
"unicode_util_compat": {:hex, :unicode_util_compat, "0.4.1", "d869e4c68901dd9531385bb0c8c40444ebf624e60b6962d95952775cac5e90cd", [:rebar3], [], "hexpm"},
"makeup": {:hex, :makeup, "0.8.0", "9cf32aea71c7fe0a4b2e9246c2c4978f9070257e5c9ce6d4a28ec450a839b55f", [:mix], [{:nimble_parsec, "~> 0.5.0", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "5fbc8e549aa9afeea2847c0769e3970537ed302f93a23ac612602e805d9d1e7f"},
"makeup_elixir": {:hex, :makeup_elixir, "0.13.0", "be7a477997dcac2e48a9d695ec730b2d22418292675c75aa2d34ba0909dcdeda", [:mix], [{:makeup, "~> 0.8", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "adf0218695e22caeda2820eaba703fa46c91820d53813a2223413da3ef4ba515"},
"metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm", "69b09adddc4f74a40716ae54d140f93beb0fb8978d8636eaded0c31b6f099f16"},
"mimerl": {:hex, :mimerl, "1.0.2", "993f9b0e084083405ed8252b99460c4f0563e41729ab42d9074fd5e52439be88", [:rebar3], [], "hexpm", "7a4c8e1115a2732a67d7624e28cf6c9f30c66711a9e92928e745c255887ba465"},
"nimble_parsec": {:hex, :nimble_parsec, "0.5.0", "90e2eca3d0266e5c53f8fbe0079694740b9c91b6747f2b7e3c5d21966bba8300", [:mix], [], "hexpm", "5c040b8469c1ff1b10093d3186e2e10dbe483cd73d79ec017993fb3985b8a9b3"},
"parse_trans": {:hex, :parse_trans, "3.3.0", "09765507a3c7590a784615cfd421d101aec25098d50b89d7aa1d66646bc571c1", [:rebar3], [], "hexpm", "17ef63abde837ad30680ea7f857dd9e7ced9476cdd7b0394432af4bfc241b960"},
"ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.4", "f0eafff810d2041e93f915ef59899c923f4568f4585904d010387ed74988e77b", [:make, :mix, :rebar3], [], "hexpm", "603561dc0fd62f4f2ea9b890f4e20e1a0d388746d6e20557cafb1b16950de88c"},
"unicode_util_compat": {:hex, :unicode_util_compat, "0.4.1", "d869e4c68901dd9531385bb0c8c40444ebf624e60b6962d95952775cac5e90cd", [:rebar3], [], "hexpm", "1d1848c40487cdb0b30e8ed975e34e025860c02e419cb615d255849f3427439d"},
}
61 changes: 46 additions & 15 deletions test/client_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -5,26 +5,33 @@ defmodule ExIRC.ClientTest do
alias ExIRC.Client
alias ExIRC.SenderInfo

test "start a client linked to the caller " do
{:ok, _} = ExIRC.start_link!()
end

test "start multiple clients" do
assert {:ok, pid} = ExIRC.start_client!
assert {:ok, pid2} = ExIRC.start_client!
assert {:ok, pid} = ExIRC.start_client!()
assert {:ok, pid2} = ExIRC.start_client!()
assert pid != pid2
end

test "client dies if owner process dies" do
test_pid = self()

pid = spawn_link(fn ->
assert {:ok, pid} = ExIRC.start_client!
send(test_pid, {:client, pid})
pid =
spawn_link(fn ->
assert {:ok, pid} = ExIRC.start_client!()
send(test_pid, {:client, pid})

receive do
:stop -> :ok
end
end)

client_pid =
receive do
:stop -> :ok
{:client, pid} -> pid
end
end)

client_pid = receive do
{:client, pid} -> pid
end

assert Process.alive?(client_pid)
send(pid, :stop)
Expand Down Expand Up @@ -60,15 +67,31 @@ defmodule ExIRC.ClientTest do

test "receiving private message sends event to handler" do
state = get_state()
msg = %ExIRC.Message{nick: "other_user", cmd: "PRIVMSG", args: [state.nick, "message"], host: "host", user: "user"}

msg = %ExIRC.Message{
nick: "other_user",
cmd: "PRIVMSG",
args: [state.nick, "message"],
host: "host",
user: "user"
}

Client.handle_data(msg, state)
expected_senderinfo = %SenderInfo{nick: "other_user", host: "host", user: "user"}
assert_receive {:received, "message", expected_senderinfo}, 10
end

test "receiving channel message sends event to handler" do
state = get_state()
msg = %ExIRC.Message{nick: "other_user", cmd: "PRIVMSG", args: ["#testchannel", "message"], host: "host", user: "user"}

msg = %ExIRC.Message{
nick: "other_user",
cmd: "PRIVMSG",
args: ["#testchannel", "message"],
host: "host",
user: "user"
}

Client.handle_data(msg, state)
expected_senderinfo = %SenderInfo{nick: "other_user", host: "host", user: "user"}
assert_receive {:received, "message", expected_senderinfo, "#testchannel"}, 10
Expand All @@ -77,7 +100,15 @@ defmodule ExIRC.ClientTest do
test "receiving channel message with mention sends events to handler" do
state = get_state()
chat_message = "hi #{state.nick}!"
msg = %ExIRC.Message{nick: "other_user", cmd: "PRIVMSG", args: ["#testchannel", chat_message], host: "host", user: "user"}

msg = %ExIRC.Message{
nick: "other_user",
cmd: "PRIVMSG",
args: ["#testchannel", chat_message],
host: "host",
user: "user"
}

Client.handle_data(msg, state)
expected_senderinfo = %SenderInfo{nick: "other_user", host: "host", user: "user"}
assert_receive {:received, chat_message, expected_senderinfo, "#testchannel"}, 10
Expand All @@ -89,7 +120,7 @@ defmodule ExIRC.ClientTest do
nick: "tester",
logged_on?: true,
event_handlers: [{self(), Process.monitor(self())}],
channels: [get_channel()],
channels: [get_channel()]
}
end

Expand Down

0 comments on commit 4b35942

Please sign in to comment.