bianchi 0.1.0 → 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +175 -21
- data/bianchi.gemspec +1 -1
- data/lib/bianchi/cli/main.rb +2 -4
- data/lib/bianchi/cli/templates/page.erb +2 -4
- data/lib/bianchi/ussd/page.rb +5 -2
- data/lib/bianchi/ussd/provider_configurations.rb +4 -23
- data/lib/bianchi/ussd/provider_parsers/africastalking.rb +28 -0
- data/lib/bianchi/version.rb +1 -1
- data/lib/bianchi.rb +1 -0
- data/ussd/engine.rb +2 -1
- data/ussd/main_menu/page_1.rb +5 -5
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 57689540f35b1837b227a23ec9b9badd237e5e1bda6b7f0b5c3d64443b404fd9
|
4
|
+
data.tar.gz: b963e56598337607ef36f0d4ebb514c9a83e354c3dc65d55d3ad299c856668b3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9272b56f5a057de49f9695cc615afcf05fbdcb14db6f31f582bf8d6b421b4dc8787ba5fbbcab87a120aaf4fd0519fad88c66f842423984915dbf271de3076207
|
7
|
+
data.tar.gz: ce5f129f37e80ff25fa642af17ae8d5f31b467f580419a7b053afe941979f6e09cf9183668a9443aea262c2d933f41a1656401098e6ef6d8d8ac26ed890b2d22
|
data/README.md
CHANGED
@@ -1,43 +1,197 @@
|
|
1
|
-
#
|
1
|
+
# Bianchi
|
2
|
+
A DSL (Domain-Specific Language) and a minimalist framework in Ruby, tailored for USSD development. Structured around a menu page approach, Bianchi offers a comprehensive suite of methods and generators, streamlining the process of building USSD applications efficiently and easily.
|
2
3
|
|
3
|
-
|
4
|
+
## Installation
|
5
|
+
Add `gem 'bianchi', '~> 0.1.0'` to your gem file and run `bundle install`
|
4
6
|
|
5
|
-
|
7
|
+
Bianchi relies on Redis and requires the `REDIS_URL` environment variable to be set, pointing to your Redis instance. Ensure Redis is installed, running, and that REDIS_URL is correctly configured before using Bianchi.
|
6
8
|
|
7
|
-
##
|
9
|
+
## Getting Started
|
10
|
+
|
11
|
+
To initialize a new USSD project, generate a project directory using Bianchi's command-line interface. Use the following Ruby command:
|
12
|
+
|
13
|
+
```ruby
|
14
|
+
bundle exec bianchi setup -p provider_name
|
15
|
+
```
|
16
|
+
|
17
|
+
Replace `provider_name` with your desired provider. Currently supported providers include: africa_is_talking.
|
18
|
+
|
19
|
+
This command creates a `ussd/engine.rb` file in the project root directory. Here's a sample content of `ussd/engine.rb`:
|
20
|
+
|
21
|
+
```ruby
|
22
|
+
module USSD
|
23
|
+
class Engine
|
24
|
+
def self.start(params)
|
25
|
+
Bianchi::USSD::Engine.start(params, provider: :africa_is_talking) do
|
26
|
+
# e.g menu :main, options
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
```
|
32
|
+
|
33
|
+
To define menus for your USSD applications' engine instance, use the following command:
|
34
|
+
|
35
|
+
```ruby
|
36
|
+
menu :menu_name, options
|
37
|
+
```
|
38
|
+
|
39
|
+
For example, let's define our first menu, which serves as the initial menu of the application, and let's call this the main menu:
|
40
|
+
|
41
|
+
```ruby
|
42
|
+
menu :main, initial: true
|
43
|
+
```
|
44
|
+
now that we have the initial menu up let's generate our pages for that menu using Bianchi's command-line interface. Use the following Ruby command:
|
45
|
+
```ruby
|
46
|
+
command: bundle exec bianchi g menu menu_name page page_number
|
47
|
+
example: bianchi g menu main page 1
|
48
|
+
```
|
49
|
+
This creates a `ussd/main_menu/page_1` file in the project root directory. Here's a sample content of the file:
|
50
|
+
|
51
|
+
```ruby
|
52
|
+
module USSD
|
53
|
+
module MainMenu
|
54
|
+
class Page1 < Bianchi::USSD::Page
|
55
|
+
def request
|
56
|
+
end
|
57
|
+
|
58
|
+
def response
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
```
|
8
64
|
|
9
|
-
|
65
|
+
In the `ussd/main_menu/page_1` file, the main application code goes into the `request` and `response` methods. Here's a sample code to illustrate the usage:
|
10
66
|
|
11
67
|
```ruby
|
12
|
-
|
68
|
+
module USSD
|
69
|
+
module MainMenu
|
70
|
+
class Page1 < Bianchi::USSD::Page
|
71
|
+
def request
|
72
|
+
render_and_await(request_body)
|
73
|
+
end
|
74
|
+
|
75
|
+
def response
|
76
|
+
case session_input_body
|
77
|
+
when "1"
|
78
|
+
redirect_to_greetings_menu_page_1
|
79
|
+
when "2"
|
80
|
+
redirect_to_repeat_menu_page_1
|
81
|
+
else
|
82
|
+
render_and_await("invalid input \n" + request_body)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
private
|
87
|
+
|
88
|
+
def request_body
|
89
|
+
<<~MSG
|
90
|
+
Welcome
|
91
|
+
1. Greetings
|
92
|
+
2. Repeat my name
|
93
|
+
MSG
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
13
98
|
```
|
14
99
|
|
15
|
-
|
100
|
+
In this example, when a page is requested, you send some information, and the end user submits data for that request. The `response` method processes the response data and can move to a new page request, end the session, or send a response from there.
|
101
|
+
### Page Renders
|
16
102
|
|
17
|
-
|
103
|
+
In Bianchi USSD applications, any method that starts with `render` sends data back to the user. Typically, we either send data and expect a response or send data and terminate the session. Here are the two main methods for rendering pages:
|
18
104
|
|
19
|
-
|
105
|
+
- `render_and_await(string)`: Responds to the user and expects the user to respond.
|
20
106
|
|
21
|
-
|
107
|
+
- `render_and_end(string)`: Responds to the user and terminates the session.
|
22
108
|
|
23
|
-
|
109
|
+
Here's how you can use these methods in Ruby:
|
110
|
+
```ruby
|
111
|
+
render_and_await("Please select an option:")
|
112
|
+
```
|
113
|
+
This method sends the message "Please select an option:" to the user and awaits their response.
|
114
|
+
```ruby
|
115
|
+
render_and_end("Thank you for using our service. Goodbye!")
|
116
|
+
```
|
117
|
+
This method sends the message "Thank you for using our service. Goodbye!" to the user and terminates the session.
|
118
|
+
|
119
|
+
### Page Session
|
120
|
+
|
121
|
+
In Bianchi USSD applications, the `session` serves as an information tracker between all the pages and records all interactions between the user and the application. It contains valuable information necessary for the USSD application to function properly.
|
122
|
+
|
123
|
+
Here are the methods available within the page session:
|
124
|
+
|
125
|
+
- `session_params`: Returns a hash containing all the data the provider sent.
|
126
|
+
- `session_input_body`: Retrieves the user's current input.
|
127
|
+
- `session_mobile_number`: Returns the current user's phone number dialing the code.
|
128
|
+
- `session_session_id`: Provides the session's ID.
|
129
|
+
- `session.store`: Accesses the current session's cache. Further details about this will be discussed later.
|
130
|
+
|
131
|
+
### Page Store
|
132
|
+
|
133
|
+
In Bianchi USSD applications, the `session.store` serves as the main application cache, where data can be stored and accessed across all pages.
|
134
|
+
|
135
|
+
Here are the methods available within the page store:
|
136
|
+
|
137
|
+
- `session.store.set(key, value)`: Stores a key-value pair in the cache.
|
138
|
+
- `session.store.get(key)`: Retrieves the value associated with a given key from the cache.
|
139
|
+
- `session.store.all`: Returns the entire cache as a hash.
|
24
140
|
|
25
|
-
|
141
|
+
Here's how you can use these methods to manage your application's cache:
|
26
142
|
|
27
|
-
|
143
|
+
```ruby
|
144
|
+
# Storing data in the cache
|
145
|
+
session.store.set('user_id', 123)
|
146
|
+
|
147
|
+
# Retrieving data from the cache
|
148
|
+
user_id = session.store.get('user_id')
|
149
|
+
|
150
|
+
# Retrieving the entire cache
|
151
|
+
all_data = session.store.all
|
152
|
+
```
|
153
|
+
|
154
|
+
### Page Redirects
|
155
|
+
|
156
|
+
In Bianchi USSD applications, any method that starts with `redirect_to` initiates another page request to be sent to the user. An example use case is when you want to transition to another menu page after processing the user's response.
|
28
157
|
|
29
|
-
|
158
|
+
For instance, in the snippet of the main menu page provided earlier, we use `redirect_to_greetings_menu_page_1` when the user selects "1". This directs the application to the greetings menu page 1's request.
|
30
159
|
|
31
|
-
|
160
|
+
Here are the methods available within the page redirects:
|
32
161
|
|
33
|
-
|
162
|
+
- `redirect_to_menu_[menu_name]_page_[page_number]`: Redirects to a specified menu using the `menu_name` and loads the page specified by `page_number`.
|
163
|
+
- `redirect_to_[next|previous]_page`: Redirects to the next or previous page within the same menu.
|
34
164
|
|
35
|
-
|
165
|
+
Here's how you can use these methods within your Bianchi USSD application:
|
36
166
|
|
37
|
-
|
167
|
+
```ruby
|
168
|
+
# Redirecting to a specific menu page
|
169
|
+
redirect_to_menu_greetings_page_1
|
170
|
+
|
171
|
+
# Redirecting to the next or previous page in the same menu
|
172
|
+
redirect_to_next_page
|
173
|
+
redirect_to_previous_page
|
174
|
+
```
|
38
175
|
|
39
|
-
|
176
|
+
## Responding to the Provider
|
40
177
|
|
41
|
-
|
178
|
+
Once all processes are completed, the next step is to prepare the response to send to the provider. The provider prompt data is accessible via the engine instance's `prompt_data` attribute.
|
42
179
|
|
43
|
-
|
180
|
+
Here's an example of how you can retrieve the provider prompt data in your USSD application:
|
181
|
+
|
182
|
+
```ruby
|
183
|
+
module USSD
|
184
|
+
class Engine
|
185
|
+
def self.start(params)
|
186
|
+
Bianchi::USSD::Engine.start(params, provider: :africa_is_talking) do
|
187
|
+
menu :main, options
|
188
|
+
menu :greetings
|
189
|
+
menu :repeat
|
190
|
+
end
|
191
|
+
end
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
# Retrieving provider prompt data
|
196
|
+
provider_data = USSD::Engine.new(provider_params).prompt_data
|
197
|
+
```
|
data/bianchi.gemspec
CHANGED
@@ -6,7 +6,7 @@ Gem::Specification.new do |spec|
|
|
6
6
|
spec.name = "bianchi"
|
7
7
|
spec.version = Bianchi::VERSION
|
8
8
|
spec.authors = ["Dapilah Sydney"]
|
9
|
-
spec.email = [
|
9
|
+
spec.email = ["dapilah.sydney@gmail.com"]
|
10
10
|
|
11
11
|
spec.summary = "Write a short summary, because RubyGems requires one."
|
12
12
|
spec.homepage = "https://github.com/SydDaps/Bianchi"
|
data/lib/bianchi/cli/main.rb
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
# rubocop:disable Metrics/ParameterLists
|
4
3
|
module Bianchi
|
5
4
|
module Cli
|
6
5
|
class Main < Thor
|
@@ -25,10 +24,10 @@ module Bianchi
|
|
25
24
|
\x5 Example: `bianchi setup -p :africa_is_talking`
|
26
25
|
|
27
26
|
LONG_DESC
|
28
|
-
method_option :provider, :
|
27
|
+
method_option :provider, aliases: "-p", type: :string, desc: "Set up ussd project for provider"
|
29
28
|
def setup
|
30
29
|
@provider = options[:provider]
|
31
|
-
unless [
|
30
|
+
unless %w[africastalking none].include? @provider
|
32
31
|
say("Error: provider #{@provider} is not yet configured.", :yellow)
|
33
32
|
exit(1)
|
34
33
|
end
|
@@ -63,4 +62,3 @@ module Bianchi
|
|
63
62
|
end
|
64
63
|
end
|
65
64
|
end
|
66
|
-
# rubocop:enable Metrics/ParameterLists
|
data/lib/bianchi/ussd/page.rb
CHANGED
@@ -29,8 +29,11 @@ module Bianchi
|
|
29
29
|
page = constant_name.safe_constantize
|
30
30
|
|
31
31
|
unless page
|
32
|
-
raise PageLoadError,
|
33
|
-
|
32
|
+
raise PageLoadError, <<~MSG
|
33
|
+
\n
|
34
|
+
#{constant_name} is supposed to be defined to process #{menu_name} menu #{page_number}.
|
35
|
+
generate menu page with `bianchi g menu #{menu_name} page #{page_number[4..]}`
|
36
|
+
MSG
|
34
37
|
end
|
35
38
|
|
36
39
|
session.menu = Menu.new(menu_name)
|
@@ -3,10 +3,12 @@
|
|
3
3
|
module Bianchi
|
4
4
|
module USSD
|
5
5
|
module ProviderConfigurations
|
6
|
+
include ProviderParsers::Africastalking
|
7
|
+
|
6
8
|
def parse_params(params)
|
7
9
|
provider_parsers = {
|
8
10
|
none: proc { params },
|
9
|
-
|
11
|
+
africastalking: proc { africastalking_params_parser(params) }
|
10
12
|
}.with_indifferent_access
|
11
13
|
|
12
14
|
parser = provider_parsers[@provider]
|
@@ -16,27 +18,10 @@ module Bianchi
|
|
16
18
|
parser.call
|
17
19
|
end
|
18
20
|
|
19
|
-
def africa_is_talking_params_parser(params)
|
20
|
-
required_params = %w[sessionId phoneNumber text serviceCode]
|
21
|
-
left_required_params = required_params - params.keys.map(&:to_s)
|
22
|
-
|
23
|
-
unless left_required_params.empty?
|
24
|
-
raise ArgumentError, "#{left_required_params} required in params to start engine for provider #{@provider}"
|
25
|
-
end
|
26
|
-
|
27
|
-
{
|
28
|
-
session_id: params["sessionId"],
|
29
|
-
mobile_number: params["phoneNumber"],
|
30
|
-
activity_state: params["text"] && params["text"].empty? ? "initial" : "subsequent",
|
31
|
-
input_body: params["text"],
|
32
|
-
service_code: params["serviceCode"]
|
33
|
-
}
|
34
|
-
end
|
35
|
-
|
36
21
|
def parser_prompt_data(prompt_data)
|
37
22
|
provider_parsers = {
|
38
23
|
none: proc { prompt_data },
|
39
|
-
|
24
|
+
africastalking: proc { africastalking_prompt_data_parser(prompt_data) }
|
40
25
|
}.with_indifferent_access
|
41
26
|
|
42
27
|
parser = provider_parsers[@provider]
|
@@ -45,10 +30,6 @@ module Bianchi
|
|
45
30
|
|
46
31
|
parser.call
|
47
32
|
end
|
48
|
-
|
49
|
-
def africa_is_talking_prompt_data_parser(prompt_data)
|
50
|
-
prompt_data["activity_state"] == :await ? "CON #{prompt_data['body']}" : "END #{prompt_data['body']}"
|
51
|
-
end
|
52
33
|
end
|
53
34
|
end
|
54
35
|
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Bianchi
|
2
|
+
module USSD
|
3
|
+
module ProviderParsers
|
4
|
+
module Africastalking
|
5
|
+
def africastalking_params_parser(params)
|
6
|
+
required_params = %w[sessionId phoneNumber text serviceCode]
|
7
|
+
left_required_params = required_params - params.keys.map(&:to_s)
|
8
|
+
|
9
|
+
unless left_required_params.empty?
|
10
|
+
raise ArgumentError, "#{left_required_params} required in params to start engine for provider #{@provider}"
|
11
|
+
end
|
12
|
+
|
13
|
+
{
|
14
|
+
session_id: params["sessionId"],
|
15
|
+
mobile_number: params["phoneNumber"],
|
16
|
+
activity_state: params["text"] && params["text"].empty? ? "initial" : "subsequent",
|
17
|
+
input_body: params["text"].split("*").last,
|
18
|
+
service_code: params["serviceCode"]
|
19
|
+
}
|
20
|
+
end
|
21
|
+
|
22
|
+
def africastalking_prompt_data_parser(prompt_data)
|
23
|
+
prompt_data["activity_state"] == :await ? "CON #{prompt_data['body']}" : "END #{prompt_data['body']}"
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
data/lib/bianchi/version.rb
CHANGED
data/lib/bianchi.rb
CHANGED
@@ -6,6 +6,7 @@ require "json"
|
|
6
6
|
require "thor"
|
7
7
|
|
8
8
|
require_relative "bianchi/version"
|
9
|
+
require_relative "bianchi/ussd/provider_parsers/africastalking"
|
9
10
|
require_relative "bianchi/ussd/provider_configurations"
|
10
11
|
require_relative "bianchi/ussd/engine"
|
11
12
|
require_relative "bianchi/ussd/menu"
|
data/ussd/engine.rb
CHANGED
data/ussd/main_menu/page_1.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: bianchi
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Dapilah Sydney
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-01-
|
11
|
+
date: 2024-01-28 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -98,6 +98,7 @@ files:
|
|
98
98
|
- lib/bianchi/ussd/page.rb
|
99
99
|
- lib/bianchi/ussd/page_delegators.rb
|
100
100
|
- lib/bianchi/ussd/provider_configurations.rb
|
101
|
+
- lib/bianchi/ussd/provider_parsers/africastalking.rb
|
101
102
|
- lib/bianchi/ussd/session.rb
|
102
103
|
- lib/bianchi/ussd/store.rb
|
103
104
|
- lib/bianchi/version.rb
|