Skip to content

Commit

Permalink
Merge pull request #31 from beamkenya/ft/documentation
Browse files Browse the repository at this point in the history
Ft/documentation
  • Loading branch information
manuelgeek authored Sep 23, 2021
2 parents 2885779 + 23c5f05 commit 625d347
Show file tree
Hide file tree
Showing 5 changed files with 129 additions and 24 deletions.
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,7 @@ npm-debug.log
# The directory NPM downloads your dependencies sources to.
/assets/node_modules/

.elixir_ls
.elixir_ls
.DS_Store
.vcode
.idea
133 changes: 117 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,47 +8,95 @@ Goals:
- Detailed error messages and documentation.
- A focus on robustness and production-level performance.

## Table of contents

### Why Use ExUssd?
- [Why Use ExUssd](#why-use-exussd)
- [Documentation](#documentation)
- [Installation](#installation)
- [Configuration](#configuration)
- [Usage](#usage)
- [Contribution](#contribution)
- [Contributors](#contributors)
- [Licence](#licence)


## Why Use ExUssd?
ExUssd lets you create simple, flexible, and customizable USSD interface.
Under the hood ExUssd uses Elixir Registry to create and route individual USSD session.

https://user-images.githubusercontent.com/23293150/124460086-95ebf080-dd97-11eb-87ab-605f06291563.mp4

## Documentation
The docs can be found at [https://hexdocs.pm/ex_ussd](https://hexdocs.pm/ex_ussd).

## Installation

To add ExUssd to your application application.
If [available in Hex](https://hex.pm/docs/publish), the package can be installed
by adding `ex_ussd` to your list of dependencies in `mix.exs`:


```elixir
defp deps do
[
{:ex_ussd, "~> 1.0.0"}
{:ex_ussd, "~> 1.0.0-rc-1"}
]
end
```

### Let's create a custom global values
## Configuration

Add to your `config.exs`

```elixir
# config/config.exs
# TODO: This are example values, replace them with your own
config :ex_ussd,
nav: [
ExUssd.Nav.new(type: :home, name: "HOME", match: "00", reverse: true, orientation: :vertical),
ExUssd.Nav.new(type: :back, name: "BACK", match: "0", right: 1),
ExUssd.Nav.new(type: :next, name: "MORE", match: "98")
ExUssd.Nav.new(type: :home, name: "HOME", match: "00", reverse: true, orientation: :vertical),
ExUssd.Nav.new(type: :back, name: "BACK", match: "0", right: 1),
ExUssd.Nav.new(type: :next, name: "MORE", match: "98")
],
delimiter: ").",
default_error: "invalid input,try again\n"
delimiter: ").",
default_error: "invalid input,try again\n"
```

## Usage

### Settable Fields

* **`:data`** Set data to pass through to next menu. N/B - ExUssd menu are stateful unless using ExUssd.new/2 with `:name` and `:resolve` as arguments;
```elixir
data = %{name: "John Doe"}
# stateful
menu
|> ExUssd.set(data: data)
|> ExUssd.add(ExUssd.new("Check Balance", &check_balance/2))

# stateless
menu
|> ExUssd.add(ExUssd.new(data: data, name: "Check Balance", resolve: &check_balance/2))
```

* **`:delimiter`** Set's menu style delimiter. Default- `:`
* **`:default_error`** Default error shown on invalid input
* **`:error`** Set custom error message
* **`:name`** Sets the name of the menu
* **`:nav`** Its used to create a new ExUssd Nav menu
* **`:orientation`** Sets the menu orientation. Available option;
- `:horizontal` - Left to right. Blog/articles style menu
- `vertical` - Top to bottom(default)
* **`:resolve`** Navigates(invokes the next `ussd_init/2`) to the next menu
* **`:should_close`** Indicate whether to USSD session should end or continue
* **`:show_navigation`** Set show navigation menu. Default - `true`
* **`:split`** Set menu batch size. Default - 7
* **`:title`** Set menu title
### ExUssd Callbacks
ExUssd provides you with 3 callbacks
* **`ussd_init/2`**
* **`ussd_init/2`**
It's invoked once when the user navigates to that particular menu

* **`ussd_callback/3`**
Expand All @@ -57,20 +105,20 @@ ExUssd provides you with 3 callbacks
* **`ussd_after_callback/3`**
It's an optional callback that is invoked after `ussd_callback/3` is invoked.


#### Example
Create a new module:

```elixir
defmodule Api.HomeResolver do
defmodule ApiWeb.HomeResolver do
use ExUssd
def ussd_init(menu, _) do
ExUssd.set(menu, title: "Enter your PIN")
end

def ussd_callback(menu, payload, %{attempt: attempt}) do
if payload.text == "5555" do
menu
|> ExUssd.set(title: "You have Entered the Secret Number, 5555")
|> ExUssd.set(should_close: true)
ExUssd.set(menu, resolve: &success_menu/2)
else
ExUssd.set(menu, error: "Wrong PIN, attempt #{attempt}/3\n")
end
Expand All @@ -81,15 +129,21 @@ defmodule Api.HomeResolver do
|> ExUssd.set(title: "Account is locked, you have entered the wrong PIN 3 times")
|> ExUssd.set(should_close: true)
end

def success_menu(menu, _) do
menu
|> ExUssd.set(title: "You have Entered the Secret Number, 5555")
|> ExUssd.set(should_close: true)
end
end
```

Lets test the different ExUssd callbacks with `ExUssd.to_string/3`
Let's test the different ExUssd callbacks with `ExUssd.to_string/3`

create menu

```elixir
menu = ExUssd.new(name: "PIN", resolve: Api.HomeResolver)
menu = ExUssd.new(name: "PIN", resolve: ApiWeb.HomeResolver)
```

**`ussd_init/2`**
Expand Down Expand Up @@ -120,6 +174,53 @@ iex> ExUssd.to_string(menu, :ussd_after_callback, [payload: %{text: "42", attemp
}}
```

### ExUssd Menu List
```elixir
defmodule HomeResolver do
use ExUssd

def product_a(menu, _payload), do: menu |> ExUssd.set(title: "selected product a")
def product_b(menu, _payload), do: menu |> ExUssd.set(title: "selected product b")
def product_c(menu, _payload), do: menu |> ExUssd.set(title: "selected product c")

def account(%{data: %{account_type: :personal}} = menu, _payload) do
menu
|> ExUssd.set(name: "personal Account")
|> ExUssd.set(resolve: &personal_account/2)
end

def account(%{data: %{account_type: :business}} = menu, _payload) do
menu
|> ExUssd.set(name: "business Account")
|> ExUssd.set(resolve: &business_account/2)
end

def check_balance(%{data: %{account_type: account_type}} = menu, _payload) do
if (account_type == :personal) do
menu
|> ExUssd.set(resolve: &personal_account_balance/2)
else
menu
|> ExUssd.set(resolve: &business_account_balance/2)
end
end

def home(menu, _payload) do
data = %{user_name: "john_doe", account_type: :personal}
menu
|> ExUssd.set(title: "Welcome")
|> ExUssd.set(data: data)
|> ExUssd.add(ExUssd.new(name: "Product A", resolve: &product_a/2))
|> ExUssd.add(ExUssd.new(name: "Product B", resolve: &product_b/2))
|> ExUssd.add(ExUssd.new(name: "Product C", resolve: &product_c/2))
|> ExUssd.add(ExUssd.new(&account/2))
|> ExUssd.add(ExUssd.new("Check Balance", &check_balance/2))
|> ExUssd.add(ExUssd.new(name: "Enter Pin", resolve: __MODULE__))
end
end
```


## Contribution

If you'd like to contribute, start by searching through the [issues](https://github.com/beamkenya/ex_ussd/issues) and [pull requests](https://github.com/beamkenya/ex_ussd/pulls) to see whether someone else has raised a similar idea or question.
Expand Down
6 changes: 3 additions & 3 deletions lib/ex_ussd.ex
Original file line number Diff line number Diff line change
Expand Up @@ -77,13 +77,13 @@ defmodule ExUssd do
end
end

defdelegate new(opts), to: ExUssd.Op
defdelegate new(name, function), to: ExUssd.Op
defdelegate set(menu, opts), to: ExUssd.Op
defdelegate add(menu, child), to: ExUssd.Op
defdelegate add(menu, menus, opts), to: ExUssd.Op
defdelegate end_session(opts), to: ExUssd.Op
defdelegate goto(opts), to: ExUssd.Op
defdelegate new(opts), to: ExUssd.Op
defdelegate new(name, function), to: ExUssd.Op
defdelegate set(menu, opts), to: ExUssd.Op
defdelegate to_string(menu, opts), to: ExUssd.Op
defdelegate to_string(menu, atom, opts), to: ExUssd.Op
end
7 changes: 4 additions & 3 deletions lib/ex_ussd/op.ex
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ defmodule ExUssd.Op do
:data,
:resolve,
:orientation,
:name
:name,
:nav
]

@doc """
Expand Down Expand Up @@ -185,7 +186,7 @@ defmodule ExUssd.Op do
```
Note: ExUssd.new callback function will be called multiple times to get the `name` value.
It's advisable to only set the `name` and `resolve` values on the callback like so reduce the side effects.
```elixir
Expand Down Expand Up @@ -340,7 +341,7 @@ defmodule ExUssd.Op do
## Example
```elixir
iex> menu = ExUssd.new(name: "home", resolve: fn menu, _payload -> menu |> ExUssd.set(title: "Welcome") end)
iex> ExUssd.to_string(menu, [])
{:ok, %{menu_string: "Welcome", should_close: false}}
Expand Down
2 changes: 1 addition & 1 deletion mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ defmodule ExUssd.MixProject do
def project do
[
app: :ex_ussd,
version: "1.0.0",
version: "1.0.0-rc-1",
elixir: "~> 1.12",
start_permanent: Mix.env() == :prod,
description: "ExUssd lets you create simple, flexible, and customizable USSD interface.",
Expand Down

0 comments on commit 625d347

Please sign in to comment.