joy_ussd_engine 0.1.0 → 0.1.5

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 29bf011074c359a280622bf3413affc10271b69305278580d5fd8159e99e25e3
4
- data.tar.gz: c8d27eff9a5f61c66c5abcdbe9329a9a8626c9a228cd4cfda93da15b9b0fbc0c
3
+ metadata.gz: 80f97294b96fb0c861b5d65226138e0f3b16e86fb71ced93371b62c2a22537b5
4
+ data.tar.gz: d28f4cfbac9b181baa531ecee3256d243b4a8c2ca09d893f7f60e55ca4221dd8
5
5
  SHA512:
6
- metadata.gz: 640a36aebd015b303bdfff567a1ba7bb8919bb82544459e026e799f3394945e094b610c4fc6f349873742fba866d86ddf950e9fde50a57c62c4200ddd1adc261
7
- data.tar.gz: 8632c6e34c5a818926832ea2f9b79528ce885f17151855341e70e526f33b78426c77e46a79a86c8f4daf8fa7bd119b4032dd7cb1e8413bee3eed83d5794e6dd7
6
+ metadata.gz: d72616d570bea38872ec4d7c6c0294aa89891e43637735f87750d8f94ee66e94b999a14ecb690b18e0b0bcb3959e740077ee8b245c7b6a57640099f61f50893e
7
+ data.tar.gz: c22582c624b22a21d6a4e711ff72ef398b5a0bc693c6125a11ec4b04a121bee943faa6926aac714129fe440c1c5700c2c42532a737701af992f740185522ef10
data/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
- ## [Unreleased]
1
+ ## [Released]
2
2
 
3
3
  ## [0.1.0] - 2022-03-12
4
4
 
5
5
  - Initial release
6
+
7
+ ## [0.1.2] - 2022-03-16
8
+
9
+ - Fixed bug in routing menu
10
+
11
+ ## [0.1.4] - 2022-04-06
12
+
13
+ - Added configs for using hubtel ussd
data/Gemfile.lock ADDED
@@ -0,0 +1,59 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ joy_ussd_engine (0.1.2)
5
+ will_paginate (~> 3.3.0)
6
+
7
+ GEM
8
+ remote: https://rubygems.org/
9
+ specs:
10
+ ast (2.4.2)
11
+ diff-lcs (1.5.0)
12
+ parallel (1.21.0)
13
+ parser (3.0.2.0)
14
+ ast (~> 2.4.1)
15
+ rainbow (3.0.0)
16
+ rake (13.0.6)
17
+ redis (4.6.0)
18
+ regexp_parser (2.2.1)
19
+ rexml (3.2.5)
20
+ rspec (3.11.0)
21
+ rspec-core (~> 3.11.0)
22
+ rspec-expectations (~> 3.11.0)
23
+ rspec-mocks (~> 3.11.0)
24
+ rspec-core (3.11.0)
25
+ rspec-support (~> 3.11.0)
26
+ rspec-expectations (3.11.0)
27
+ diff-lcs (>= 1.2.0, < 2.0)
28
+ rspec-support (~> 3.11.0)
29
+ rspec-mocks (3.11.0)
30
+ diff-lcs (>= 1.2.0, < 2.0)
31
+ rspec-support (~> 3.11.0)
32
+ rspec-support (3.11.0)
33
+ rubocop (1.20.0)
34
+ parallel (~> 1.10)
35
+ parser (>= 3.0.0.0)
36
+ rainbow (>= 2.2.2, < 4.0)
37
+ regexp_parser (>= 1.8, < 3.0)
38
+ rexml
39
+ rubocop-ast (>= 1.9.1, < 2.0)
40
+ ruby-progressbar (~> 1.7)
41
+ unicode-display_width (>= 1.4.0, < 3.0)
42
+ rubocop-ast (1.11.0)
43
+ parser (>= 3.0.1.1)
44
+ ruby-progressbar (1.11.0)
45
+ unicode-display_width (1.8.0)
46
+ will_paginate (3.3.1)
47
+
48
+ PLATFORMS
49
+ -darwin-20
50
+
51
+ DEPENDENCIES
52
+ joy_ussd_engine!
53
+ rake (~> 13.0)
54
+ redis
55
+ rspec
56
+ rubocop (~> 1.7)
57
+
58
+ BUNDLED WITH
59
+ 2.2.21
data/README.md CHANGED
@@ -29,6 +29,9 @@ A ruby library for building text based applications rapidly. It supports buildin
29
29
  - [PaginateMenu Properties](#paginatemenu-properties)
30
30
  - [PaginateMenu Methods](#paginatemenu-methods)
31
31
  - [PaginateMenu Example](#paginatemenu-example)
32
+ - [Transformer Configs](#transformer-configs)
33
+ - [Hubtel Transformer](#hubtel-transformer)
34
+ - [Twilio Transformer](#twilio-transformer)
32
35
  - [Development](#development)
33
36
  - [Contributing](#contributing)
34
37
  - [License](#license)
@@ -86,7 +89,7 @@ class MyController < ApplicationController
86
89
  skip_before_action :verify_authenticity_token
87
90
 
88
91
  def create
89
- joy_ussd_engine = JoyUssdEngine::Core.new(ussd_params, Transformers::HubtelTransformer, start_point: Ussd::Menus::StartMenu, end_point: Ussd::Menus::EndMenu)
92
+ joy_ussd_engine = JoyUssdEngine::Core.new(ussd_params, Ussd::Transformers::HubtelTransformer, start_point: Ussd::Menus::StartMenu, end_point: Ussd::Menus::EndMenu)
90
93
  response = joy_ussd_engine.process
91
94
  render json: response, status: :created
92
95
  end
@@ -99,12 +102,12 @@ end
99
102
 
100
103
  The `JoyUssdEngine::Core.new` class takes the following parameters.<a id="params"></a>
101
104
 
102
- | Parameter | Type | Description |
103
- | -------------------------------- | ----- | ------------------------------------------------------------------------------------------------------------------------------------ |
104
- | params | hash | Params coming from a post end point in a rails controller |
105
- | [data_transformer](#transformer) | class | A class to transform the incoming and outgoing request between a particular provider and `JoyUssdEngine` |
106
- | [start_point](#menu) | class | Points to a menu that starts the application. This menu is the first menu that loads when the app starts |
107
- | [end_point](#menu) | class | This menu will terminate the ussd session if a particular provider (`data_transformer`) returns true in the `app_terminator` method. |
105
+ | Parameter | Type | Description |
106
+ | ------------------------------------ | ----- | ------------------------------------------------------------------------------------------------------------------------------------ |
107
+ | params | hash | Params coming from a post end point in a rails controller |
108
+ | [data_transformer](#datatransformer) | class | A class to transform the incoming and outgoing request between a particular provider and `JoyUssdEngine` |
109
+ | [start_point](#menu) | class | Points to a menu that starts the application. This menu is the first menu that loads when the app starts |
110
+ | [end_point](#menu) | class | This menu will terminate the ussd session if a particular provider (`data_transformer`) returns true in the `app_terminator` method. |
108
111
 
109
112
  ### Generators
110
113
 
@@ -117,7 +120,7 @@ The rails terminal is very powerful and we can utilize it to generate menus easi
117
120
 
118
121
  ### DataTransformer
119
122
 
120
- A data transformer transforms the incoming request and outgoing response between a particular provider and the `JoyUssdEngine` so they can effectively communicate between each other. The `JoyUssdEngine` can accept any request object but there are two required fields that needs to be present for it to work properly. The required fields are `session_id` and `message`. This is why the `DataTransformer` is needed to convert the request so it can provide this two required fields (`session_id`, `message`).
123
+ A data transformer transforms the incoming request and outgoing response between a particular provider and the `JoyUssdEngine` so they can effectively communicate with each other. The `JoyUssdEngine` can accept any request object but there are two required fields that needs to be present for it to work properly. The required fields are `session_id` and `message`. This is why the `DataTransformer` is needed to convert the request so it can provide this two required fields (`session_id`, `message`).
121
124
 
122
125
  #### Methods
123
126
 
@@ -134,7 +137,7 @@ A data transformer transforms the incoming request and outgoing response between
134
137
  When using `hubtel` we need to convert the `hubtel` request into what the `JoyUssdEngine` expects with the `request_params` method. Also we need to convert the response back from `JoyUssdEngine` to `hubtel` with the `response` and `release` methods. With this approach we can easily extend the `JoyUssdEngine` to target multiple providers like (Twilio, Telegram, etc) with ease. The `app_terminator` returns a boolean and terminates the app when a particular condition is met(For example: On whatsapp the user sends a message with text `end` to terminate the app)
135
138
 
136
139
  ```ruby
137
- class HubtelTransformer < JoyUssdEngine::DataTransformer
140
+ class Ussd::Transformers::HubtelTransformer < JoyUssdEngine::DataTransformer
138
141
  # Transforms request payload between hubtel and our application
139
142
  # The session_id and message fields are required so we get them from hubtel (session_id: params[:Mobile] and message: params[:Message]).
140
143
  # And we pass in other hubtel specific params like (ClientState: params[:ClientState], Type: params[:Type])
@@ -183,19 +186,19 @@ end
183
186
 
184
187
  ### Menu
185
188
 
186
- Menus are simply the views for our application. They contain the code for rendering the text and menus that display on the user's device. Also they contain the business logic for your app.
189
+ Menus are simply the views for our application. They contain the code for rendering the text that display on the user's device. Also they contain the business logic for your app.
187
190
 
188
191
  #### Menu Properties
189
192
 
190
- | Properties | Type | Description |
191
- | ------------ | ---------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- |
192
- | context | object | Provides methods for setting and getting state values |
193
- | field_name\* | string | The name for a particular input field. This name can be used to later retrieve the value the user entered in that field. (**Required**) |
194
- | menu_text\* | string | The text to display to the user. (**Required**) |
195
- | error_text | string | If there is an error you will have to set the error message here. (**Optional**) |
196
- | skip_save | boolean | If set to true the user input will not be saved. **Default: false** (**Optional**) |
197
- | menu_items | array <{title: '', menu: JoyUssdEngine::Menu}> | Stores an array of menu items. |
198
- | field_error | boolean | If set to true it will route back to the menu the error was caught in for the user to input the correct values. |
193
+ | Properties | Type | Description |
194
+ | ------------- | ----------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- |
195
+ | @context | object | Provides methods for setting and getting state values |
196
+ | @field_name\* | string | The name for a particular input field. This name can be used to later retrieve the value the user entered in that field. (**Required**) |
197
+ | @menu_text\* | string | The text to display to the user. (**Required**) |
198
+ | @error_text | string | If there is an error you will have to set the error message here. (**Optional**) |
199
+ | @skip_save | boolean | If set to true the user input will not be saved. **Default: false** (**Optional**) |
200
+ | @menu_items | array <{title: '', route: JoyUssdEngine::Menu}> | Stores an array of menu items with their corresponding routes. |
201
+ | @field_error | boolean | If set to true it will route back to the menu the error was caught in for the user to input the correct values. |
199
202
 
200
203
  #### Lifecycle Methods
201
204
 
@@ -209,25 +212,25 @@ Menus are simply the views for our application. They contain the code for render
209
212
 
210
213
  #### Render Methods
211
214
 
212
- | Methods | Parameters | Description |
213
- | ------------ | ---------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- |
214
- | joy_response | Menu | This method takes a single argument (which is a class that points to the next menu) and is used to render out a menu to the user. |
215
- | joy_release | none | This method renders a text to the user and ends the users session |
216
- | load_menu | Menu | This method takes a single argument (which is a class that points to the next menu) and is used with the Routing and Paginating Menus to render out menu items. |
215
+ | Methods | Parameters | Description |
216
+ | ------------ | ---------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
217
+ | joy_response | Menu | This method takes a single argument (which is a class that points to the next menu) and is used to render out the text stored in the `@menu_text` variable to the user. |
218
+ | joy_release | none | This method renders the text in the `@menu_text` variable to the user and ends the users session |
219
+ | load_menu | Menu | This method takes a single argument (which is a class that points to the next menu) and is used with the [Routing](#routing-menus) and [Paginating](#paginatemenu) Menus to render out menu items. |
217
220
 
218
221
  #### Other Methods
219
222
 
220
- | Methods | Description |
221
- | ----------------- | ---------------------------------------------------------------------- |
222
- | show_menu | Returns a list of menus stored in the `menu_items` variable |
223
- | get_selected_item | Gets the selected menu from the `menu_items` array |
224
- | raise_error | Takes an error message as an arguments and ends the user session |
225
- | has_selected? | Checks if the user has selected an item in from the `menu_items` array |
223
+ | Methods | Description |
224
+ | ----------------- | --------------------------------------------------------------------------------------------------------------------------------------- |
225
+ | show_menu | Returns the menu text to be rendered out. This method is used with only [Routing](#routing-menus) and [Paginating](#paginatemenu) Menus |
226
+ | get_selected_item | Gets the users selection from the `@menu_items` array |
227
+ | raise_error | Takes an error message as an arguments and renders the message to the user before ending the user's session |
228
+ | has_selected? | Checks if the user has selected an item from the `@menu_items` array |
226
229
 
227
230
  #### Create a menu
228
231
 
229
232
  ```ruby
230
- class Menus::MainMenu < JoyUssdEngine::Menu
233
+ class Ussd::Menus::MainMenu < JoyUssdEngine::Menu
231
234
  def on_validate
232
235
  # use this method to validate the user's input
233
236
 
@@ -264,7 +267,7 @@ class Menus::MainMenu < JoyUssdEngine::Menu
264
267
  # Render ussd menu here
265
268
 
266
269
  # the joy_response renders out the ussd menu and takes the class of the next menu to route to as an argument.
267
- joy_response(Menus::NextMenu)
270
+ joy_response(Ussd::Menus::NextMenu)
268
271
  end
269
272
  end
270
273
  ```
@@ -280,30 +283,35 @@ When the user enters a value which is not the string `"john doe"` an error will
280
283
  #### Execution Order of Lifecycle Methods
281
284
 
282
285
  - before_render
283
- ***
284
- This is the first method that gets executed. It is used for querying the db and handling the business logic of our application. This method also is used to set the text (`menu_text`) to be rendered and the input name (`field_name`) for the current menu.
285
- - on_validate
286
286
 
287
287
  ***
288
288
 
289
- This method will be executed when the user submits a response. We use this method to validate the user's input and set an error_message to display when there is an error. Normally we will set `field_error` value to true and store the error message in `error_text`. Then we can later access the error_message in the `on_error` lifecycle method and append the error to `menu_text` so it will be rendered out to the user.
289
+ This is the first method that gets executed. It is used for querying the db and handling the business logic of our application. This method also is used to set the text (`@menu_text`) to be rendered and the input name (`@field_name`) for the current menu.
290
290
 
291
291
  - on_error
292
292
 
293
293
  ***
294
294
 
295
- This is the next method that gets executed and it is used to set error messages. It will only be executed if the `field_error` value is set to true.
295
+ This is the next method that gets executed and it is used to set error messages. It will only be executed if the `@field_error` value is set to true.
296
296
 
297
297
  - render
298
298
 
299
299
  ***
300
300
 
301
- This method is used for rendering out the menu by using the text stored in the `menu_text` variable. There are only three methods that should be used in the render method. Which are [joy_release](#render_methods), [joy_response](#render_methods), and [load_menu](render_methods).
301
+ This method is used for rendering out the menu by using the text stored in the `@menu_text` variable. There are only three methods that should be used in the render method. Which are [joy_release](#render-methods), [joy_response](#render-methods), and [load_menu](render-methods).
302
302
 
303
303
  - after_render
304
+
304
305
  ***
306
+
305
307
  Use this method to do any other business logic after the menu has been rendered out and awaiting user response.
306
308
 
309
+ - on_validate
310
+
311
+ ***
312
+
313
+ This method will be executed when the user submits a response. We use this method to validate the user's input and set an error_message to display when there is an error. Normally we will set `@field_error` value to true and store the error message in `@error_text`. Then we can later access the error_message in the `on_error` lifecycle method and append the error message to `@menu_text` so it will be rendered out to the user.
314
+
307
315
  #### Execution Order Diagram
308
316
 
309
317
  The Diagram below shows how these methods are executed
@@ -312,7 +320,7 @@ The Diagram below shows how these methods are executed
312
320
 
313
321
  #### Get Http Post Data
314
322
 
315
- We can access the post request data coming from the rails controller in any menu with the `context` object. The `context` object can be used to access data by reading values from the `params` hash of a post request. This hash consist of the `session_id`, `message` and any other additional data returned by the `request_params` method in the [DataTransformer](#transformer) class.
323
+ We can access the post request data coming from the rails controller in any menu with the `@context` object. The `@context` object can be used to access post data by reading values from the `params` hash of a post request. This hash consist of the `session_id`, `message` and any other additional data returned by the `request_params` method in the [DataTransformer](#datatransformer) class.
316
324
 
317
325
  ```ruby
318
326
  # Just call @context.params[key] to access a particular value coming from a post request made available to our app through the DataTransformer.request_params method.
@@ -322,7 +330,7 @@ We can access the post request data coming from the rails controller in any menu
322
330
 
323
331
  #### Saving and Accessing Data
324
332
 
325
- We can save and access data in any menu with the `context` object. The `context` object has two methods `set_state` and `get_state` which are used for saving and retrieving data. The saved data will be destroyed once the user session ends or is expired and it is advisable to persist this data into a permanent storage like a database if you will need it after the user session has ended.
333
+ We can save and access data in any menu with the `@context` object. The `@context` object has two methods `set_state` and `get_state` which are used for saving and retrieving data. The saved data will be destroyed once the user session ends or is expired and it is advisable to persist this data into a permanent storage like a database if you will need it after the user session has ended.
326
334
 
327
335
  ```ruby
328
336
  # Just call @context.set_state(key: value) to set a key with a particular value
@@ -332,9 +340,9 @@ We can save and access data in any menu with the `context` object. The `context`
332
340
  @context.get_state[:selected_book]
333
341
  ```
334
342
 
335
- Also by default any menu that has the `field_name` variable set. Will automatically save the users input with a key matching the string stored in the `field_name` variable.
343
+ Also by default any menu that has the `@field_name` variable set. Will automatically save the users input with a key matching the string stored in the `@field_name` variable.
336
344
 
337
- **Note:** However if the `skip_save` variable is set to true the user input will not be store for that particular menu. By default this value is false.
345
+ **Note:** However if the `@skip_save` variable is set to true the user input will not be store for that particular menu. By default this value is false.
338
346
 
339
347
  ```ruby
340
348
  # This stores the name of the input field for this menu
@@ -355,11 +363,11 @@ We can throw an error with a message and terminate the user session any where in
355
363
  return raise_error("Sorry something went wrong!")
356
364
  ```
357
365
 
358
- There is also another way to handle errors without ending or terminating the user session. We can use the `on_validate` lifecycle method to validate user input and when there is an error we set the `field_error` variable to true and the `error_text` variable to include the error message.
366
+ There is also another way to handle errors without ending or terminating the user session. We can use the `on_validate` lifecycle method to validate user input and when there is an error we set the `@field_error` variable to true and the `@error_text` variable to include the error message.
359
367
 
360
- Then in the `on_error` lifecycle method we can append the `error_text` variable to the `menu_text` variable so it displays on the screen for the user.
368
+ Then in the `on_error` lifecycle method we can append the `@error_text` variable to the `@menu_text` variable so it displays the error when render an output to the user.
361
369
 
362
- **Note:** The `on_error` method will only be invoke if the `field_error` variable is set to true.
370
+ **Note:** The `on_error` method will only be invoke if the `@field_error` variable is set to true.
363
371
 
364
372
  [View the example code on error handling here](#create_menu)
365
373
 
@@ -369,7 +377,7 @@ You can show a list of menu items with their corresponding routes. When the user
369
377
  When the user selects a menu that is not in the list an error is displayed to the user and the user session wil be terminated.
370
378
 
371
379
  ```ruby
372
- class Menus::InitialMenu < JoyUssdEngine::Menu
380
+ class Ussd::Menus::InitialMenu < JoyUssdEngine::Menu
373
381
 
374
382
  def before_render
375
383
  # Implement before call backs
@@ -378,8 +386,8 @@ class Menus::InitialMenu < JoyUssdEngine::Menu
378
386
 
379
387
  # Store a list of menu items with their routes
380
388
  @menu_items = [
381
- {title: 'Make Payments', route: Menus::MakePayments},
382
- {title: 'View Transaction', route: Menus::ViewTransaction}
389
+ {title: 'Make Payments', route: Ussd::Menus::MakePayments},
390
+ {title: 'View Transaction', route: Ussd::Menus::ViewTransaction}
383
391
  ]
384
392
 
385
393
  # Show menu items on screen with the show_menu method.
@@ -390,8 +398,8 @@ class Menus::InitialMenu < JoyUssdEngine::Menu
390
398
  def render
391
399
  # Render ussd menu here
392
400
 
393
- # Use this to load the menu the user selects
394
- # get_selected_item automatically listens to user inputs and passes the selected menu into load_menu method
401
+ # Use the `load_menu` method to load the menus on screen
402
+ # The `get_selected_item` method automatically listens to user inputs and passes the selected menu into the `load_menu` method
395
403
  load_menu(get_selected_item)
396
404
  end
397
405
  end
@@ -401,10 +409,10 @@ This will be rendered out when this menu is executed
401
409
 
402
410
  ![MenuRoutes](./images/menu_items_routes.png)
403
411
 
404
- If the `Menus::ViewTransaction` has a structure like this.
412
+ If the `Ussd::Menus::ViewTransaction` has a structure like this.
405
413
 
406
414
  ```ruby
407
- class Menus::ViewTransaction < JoyUssdEngine::Menu
415
+ class Ussd::Menus::ViewTransaction < JoyUssdEngine::Menu
408
416
 
409
417
  def before_render
410
418
  # Implement before call backs
@@ -419,11 +427,11 @@ class Menus::ViewTransaction < JoyUssdEngine::Menu
419
427
  end
420
428
  ```
421
429
 
422
- When the user enters 2 in the `Menus::InitialMenu` menu then the following will be rendered and the user session will be terminated.
430
+ When the user enters 2 in the `Ussd::Menus::InitialMenu` menu then the following will be rendered and the user session will be terminated.
423
431
 
424
432
  ![transaction](./images/transactions_menu.png)
425
433
 
426
- The `Menus::ViewTransaction` menu uses the `joy_release` method to render out the text stored in the `@menu_text` variable and ends the user session.
434
+ The `Ussd::Menus::ViewTransaction` menu uses the `joy_release` method to render out the text stored in the `@menu_text` variable and ends the user session.
427
435
 
428
436
  ### PaginateMenu
429
437
 
@@ -434,12 +442,12 @@ A `PaginateMenu` has all the properties and methods in a `Menu` in addition to t
434
442
 
435
443
  A `PaginateMenu` has the following properties in addition properties in [Menu](#menu).
436
444
 
437
- | Properties | Type | Description |
438
- | ---------------- | ----------- | ------------------------------------------------------------------------- |
439
- | paginating_items | array <any> | Stores an array of items to paginate on a particular menu. |
440
- | items_per_page | integer | The number of items to show per page. **Default: 5** |
441
- | back_key | string | A string holding the input value for navigating back. **Default: '0'** |
442
- | next_key | string | A string holding the input value for navigating forward. **Default: '#'** |
445
+ | Properties | Type | Description |
446
+ | ----------------- | ----------- | ------------------------------------------------------------------------- |
447
+ | @paginating_items | array <any> | Stores an array of items to paginate on a particular menu. |
448
+ | @items_per_page | integer | The number of items to show per page. **Default: 5** |
449
+ | @back_key | string | A string holding the input value for navigating back. **Default: '0'** |
450
+ | @next_key | string | A string holding the input value for navigating forward. **Default: '#'** |
443
451
 
444
452
  #### PaginateMenu Methods
445
453
 
@@ -453,7 +461,7 @@ A `PaginateMenu` has the following properties in addition properties in [Menu](#
453
461
  #### PaginateMenu Example
454
462
 
455
463
  ```ruby
456
- class Menus::Books < JoyUssdEngine::PaginateMenu
464
+ class Ussd::Menus::Books < JoyUssdEngine::PaginateMenu
457
465
 
458
466
  def before_render
459
467
  # Implement before call backs
@@ -470,23 +478,24 @@ A `PaginateMenu` has the following properties in addition properties in [Menu](#
470
478
  {title: "Design Patterns In C#", item: {id: 8}}
471
479
  ]
472
480
 
473
- # The paginate methods returns a list of paginated list for the current page when it is called
481
+ # The `paginate` method returns a list of paginated items for the current page when it is called
474
482
  paginated_list = paginate
475
483
 
476
- # In a PaginateMenu the show_menu take a list a two optional named parameter values (title,key).
484
+ # In a PaginateMenu the `show_menu` method takes a list and two optional named parameter values (title,key).
477
485
 
478
486
  # The title shows the page title for the menu.
479
487
 
480
- # The key stores the key of the hash which contains the text to be rendered on each list item.
488
+ # The key stores the key of the hash which contains the text to be rendered for each list item.
481
489
 
482
490
  # If the key is not set the paginating_items is treated as a string and rendered to the user.
491
+
483
492
  # eg: @paginating_items = ["Data Structures","Excel Programming","Economics","Big Bang","Democracy Building","Python for Data Scientist","Money Mind","Design Patterns In C#"]
484
493
 
485
494
  @menu_text = show_menu(paginated_list, title: 'My Books', key: 'title')
486
495
 
487
496
  # In other to select a paginating item we have to wrap the selection logic in an if has_selected? block to prevent some weird errors.
488
497
  if has_selected?
489
- # the get_selected_item is used to get the selected item from the paginating menu
498
+ # the get_selected_item is used to get the selected item from the paginating list
490
499
  selected_book = get_selected_item
491
500
 
492
501
  # We save the selected book so we can access later
@@ -498,14 +507,14 @@ A `PaginateMenu` has the following properties in addition properties in [Menu](#
498
507
  # Render ussd menu here
499
508
 
500
509
  # The load_menu function points to a menu to load when a book is selected.
501
- load_menu(Menus::ShowBook)
510
+ load_menu(Ussd::Menus::ShowBook)
502
511
  end
503
512
  end
504
513
  ```
505
514
 
506
- To use a `PaginateMenu` we have to store the items to be paginated in the `paginating_items` variable. Then we call the `paginate` method and store the result in a variable. We can now pass the variable into the `show_menu` method and specify a `title` for the page if we have any. The `show_menu` method can also accept a `key` which is used to get the key containing the string to be rendered in a paginating_item. If the `key` is left blank the `paginating_items` are treated as strings and rendered automatically.
515
+ To use a `PaginateMenu` we have to store the items to be paginated in the `@paginating_items` variable. Then we call the `paginate` method and store the result in a variable (`paginated_list`). We can now pass the variable (`paginated_list`) into the `show_menu` method and specify a `title` for the page if we have any. The `show_menu` method can also accept a `key` which is used to get the key containing the string to be rendered in a paginating_item. If the `key` is left blank the `@paginating_items` are treated as strings and rendered automatically.
507
516
 
508
- In order to get the item the user selected we have to wrap the selection login in an `if has_selected?` block to prevent some weird errors, then we can access the selected item with the `get_selected_item` method.
517
+ In order to get the item the user has selected we have to wrap the selection login in an `if has_selected?` block to prevent some weird errors, then we can access the selected item with the `get_selected_item` method.
509
518
 
510
519
  The following screenshots shows the paginating menu when it's first rendered.
511
520
 
@@ -515,10 +524,10 @@ When the user enters '#' we move to the next page in the list.
515
524
 
516
525
  ![paginate_menu2](./images/paginate_menu2.png)
517
526
 
518
- In the next menu (`Menus::ShowBook`) we have code that looks like this.
527
+ In the next menu (`Ussd::Menus::ShowBook`) we have code that looks like this.
519
528
 
520
529
  ```ruby
521
- class Menus::ShowBook < Ussd::Menu
530
+ class Ussd::Menus::ShowBook < JoyUssdEngine::Menu
522
531
  def before_render
523
532
  # Implement before call backs
524
533
  book = @context.get_state[:selected_book]
@@ -540,6 +549,108 @@ When th user selects an item in the `PaginateMenu` we get the users selection wi
540
549
 
541
550
  ![paginate_item_select](images/paginate_item_selected.png)
542
551
 
552
+ ## Transformer Configs
553
+
554
+ Transformer configs for various providers. This configs can be used if you use any of these providers.
555
+
556
+ ### Hubtel Transformer
557
+
558
+ ```ruby
559
+ class HubtelTransformer < JoyUssdEngine::DataTransformer
560
+ # Tranforms request and response payload between hubtel and our application
561
+ def request_params(params)
562
+ {
563
+ session_id: params[:Mobile],
564
+ message: params[:Message],
565
+ ClientState: params[:ClientState],
566
+ Type: params[:Type]
567
+ data: params
568
+ }
569
+ end
570
+
571
+ def app_terminator(params)
572
+ params[:Type] == 'Release' || (params[:Type] != "Initiation" && @context.get_state.blank?)
573
+ end
574
+
575
+ def response(message, client_state)
576
+ {
577
+ Type: "Response",
578
+ Message: message,
579
+ ClientState: client_state
580
+ }
581
+ end
582
+
583
+ def release(message)
584
+ {
585
+ Type: "Release",
586
+ Message: message,
587
+ ClientState: "End"
588
+ }
589
+ end
590
+
591
+ def expiration
592
+ 60.seconds
593
+ end
594
+ end
595
+ ```
596
+
597
+ ### Twilio Transformer
598
+
599
+ ```ruby
600
+ class TwilioTransformer < JoyUssdEngine::DataTransformer
601
+ ACCOUNT_SID = "932843hwhjewhje7388"
602
+ AUTH_TOKEN = "3473847hewjrejrheeee"
603
+
604
+ def client
605
+ @client ||= Twilio::REST::Client.new(ACCOUNT_SID, AUTH_TOKEN)
606
+ end
607
+
608
+ # Tranforms request and response payload between twilio and our application
609
+ def request_params(params)
610
+ {
611
+ session_id: "0#{params[:From].last(9)}",
612
+ message: params[:Body],
613
+ Mobile: "0#{params[:From].last(9)}",
614
+ data: params
615
+ }
616
+ end
617
+
618
+ def app_terminator(params)
619
+ params[:Body] == 'end'
620
+ end
621
+
622
+ def response(message, client_state)
623
+ client.messages.create(
624
+ from: from,
625
+ to: to,
626
+ body: message
627
+ )
628
+ {message: message}
629
+ end
630
+
631
+ def release(message)
632
+ client.messages.create(
633
+ from: from,
634
+ to: to,
635
+ body: message
636
+ )
637
+ {message: message}
638
+ end
639
+
640
+ def expiration
641
+ # set expiration for different providers
642
+ end
643
+
644
+ def to
645
+ "whatsapp:+233#{@context.params[:Mobile].last(9)}"
646
+ end
647
+
648
+ def from
649
+ 'whatsapp:+14155238886'
650
+ end
651
+ end
652
+ ```
653
+
543
654
  ## Development
544
655
 
545
656
  After checking out the repo, run `bin/setup` to install dependencies. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
data/images/lifecycle.jpg CHANGED
Binary file
@@ -22,9 +22,14 @@ Gem::Specification.new do |spec|
22
22
 
23
23
  # Specify which files should be added to the gem when it is released.
24
24
  # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
25
- spec.files = Dir.chdir(File.expand_path(__dir__)) do
26
- `git ls-files -z`.split("\x0").reject { |f| f.match(%r{\A(?:test|spec|features)/}) }
25
+ spec.files = Dir['**/*'] do
26
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{\A(?:test|spec|features|images)/}) }
27
27
  end
28
+
29
+ # spec.files = `git ls-files -z`.split("\x0").reject do |f|
30
+ # f.match(%r{^(test|spec|features)/})
31
+ # end
32
+
28
33
  spec.bindir = "exe"
29
34
  spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
30
35
  spec.require_paths = ["lib"]
@@ -32,6 +37,8 @@ Gem::Specification.new do |spec|
32
37
  # Uncomment to register a new dependency of your gem
33
38
  # spec.add_dependency "example-gem", "~> 1.0"
34
39
  spec.add_dependency "will_paginate", "~> 3.3.0"
40
+ spec.add_development_dependency "rspec"
41
+ spec.add_development_dependency "redis"
35
42
 
36
43
  # For more information and examples about making a new gem, checkout our
37
44
  # guide at: https://bundler.io/guides/creating_gem.html
@@ -8,7 +8,7 @@ module JoyUssdEngine
8
8
  # what our application can understand
9
9
  attr_reader :context
10
10
  def initialize(context)
11
- @context = context
11
+ @context = context
12
12
  end
13
13
 
14
14
  def request_params(params)
@@ -30,6 +30,7 @@ module JoyUssdEngine
30
30
 
31
31
  def expiration
32
32
  # set expiration for different providers
33
+ @context.expiration.blank? ? 60.seconds : @expiration
33
34
  end
34
35
  end
35
36
  end
@@ -0,0 +1,35 @@
1
+
2
+ module JoyUssdEngine
3
+ class HubtelTransformer < JoyUssdEngine::DataTransformer
4
+ # Tranforms request and response payload between hubtel and our application
5
+ def request_params(params)
6
+ {
7
+ session_id: params[:Mobile],
8
+ message: params[:Message],
9
+ ClientState: params[:ClientState],
10
+ Type: params[:Type],
11
+ data: params
12
+ }
13
+ end
14
+
15
+ def app_terminator(params)
16
+ params[:Type] == 'Release' || (params[:Type] != "Initiation" && @context.get_state.blank?)
17
+ end
18
+
19
+ def response(message, client_state)
20
+ {
21
+ Type: "Response",
22
+ Message: message,
23
+ ClientState: client_state
24
+ }
25
+ end
26
+
27
+ def release(message)
28
+ {
29
+ Type: "Release",
30
+ Message: message,
31
+ ClientState: "EndJoyUssdEngine"
32
+ }
33
+ end
34
+ end
35
+ end
@@ -31,7 +31,7 @@ module JoyUssdEngine
31
31
  def joy_release(error_message = "")
32
32
  @context.reset_state
33
33
  {
34
- ClientState: "EndJoyUssdEngineiuewhjsdj",
34
+ ClientState: "EndJoyUssdEngine",
35
35
  data: @context.selected_provider.send("release", error_message.blank? ? @menu_text : error_message)
36
36
  }
37
37
  end
@@ -43,7 +43,7 @@ module JoyUssdEngine
43
43
  @context.set_state({"#{@current_client_state}_show_menu_initiation".to_sym => nil})
44
44
  set_previous_state
45
45
  @context.current_menu = @current_client_state = next_menu
46
- @context.load_menu(next_menu)
46
+ @context.load_from_paginate_menu(next_menu)
47
47
  end
48
48
  end
49
49
 
@@ -156,8 +156,8 @@ module JoyUssdEngine
156
156
  response = render
157
157
  response = response.blank? ? joy_response(current_client_state) : response
158
158
  after_render
159
- save_state(response[:ClientState]) if response[:ClientState] != "EndJoyUssdEngineiuewhjsdj"
160
- @context.reset_state if response[:ClientState] == "EndJoyUssdEngineiuewhjsdj"
159
+ save_state(response[:ClientState]) if response[:ClientState] != "EndJoyUssdEngine"
160
+ @context.reset_state if response[:ClientState] == "EndJoyUssdEngine"
161
161
  response[:data].blank? ? response : response[:data]
162
162
  end
163
163
 
@@ -193,8 +193,8 @@ module JoyUssdEngine
193
193
  response = render
194
194
  response = response.blank? ? joy_response(@current_client_state) : response
195
195
  after_render
196
- save_state(response[:ClientState]) if response[:ClientState] != "EndJoyUssdEngineiuewhjsdj"
197
- @context.reset_state if response[:ClientState] == "EndJoyUssdEngineiuewhjsdj"
196
+ save_state(response[:ClientState]) if response[:ClientState] != "EndJoyUssdEngine"
197
+ @context.reset_state if response[:ClientState] == "EndJoyUssdEngine"
198
198
  response
199
199
  end
200
200
 
@@ -210,8 +210,8 @@ module JoyUssdEngine
210
210
  response = render
211
211
  response = response.blank? ? joy_response(@current_client_state) : response
212
212
  after_render
213
- save_state(response[:ClientState]) if response[:ClientState] != "EndJoyUssdEngineiuewhjsdj"
214
- @context.reset_state if response[:ClientState] == "EndJoyUssdEngineiuewhjsdj"
213
+ save_state(response[:ClientState]) if response[:ClientState] != "EndJoyUssdEngine"
214
+ @context.reset_state if response[:ClientState] == "EndJoyUssdEngine"
215
215
  response[:data].blank? ? response : response[:data]
216
216
  end
217
217
  end
@@ -185,8 +185,8 @@ module JoyUssdEngine
185
185
  response = render
186
186
  response = response.blank? ? joy_response(@current_client_state) : response
187
187
  after_render
188
- save_state(response[:ClientState]) if response[:ClientState] != "EndJoyUssdEngineiuewhjsdj"
189
- @context.reset_state if response[:ClientState] == "EndJoyUssdEngineiuewhjsdj"
188
+ save_state(response[:ClientState]) if response[:ClientState] != "EndJoyUssdEngine"
189
+ @context.reset_state if response[:ClientState] == "EndJoyUssdEngine"
190
190
  response[:data].blank? ? response : response[:data]
191
191
  end
192
192
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module JoyUssdEngine
4
- VERSION = "0.1.0"
4
+ VERSION = "0.1.5"
5
5
  end
@@ -5,6 +5,7 @@ require 'joy_ussd_engine/menu'
5
5
  require 'joy_ussd_engine/paginate_menu'
6
6
  require 'joy_ussd_engine/data_transformer'
7
7
  require 'joy_ussd_engine/session_manager'
8
+ require 'joy_ussd_engine/hubtel_transformer'
8
9
 
9
10
 
10
11
  module JoyUssdEngine
@@ -13,11 +14,12 @@ module JoyUssdEngine
13
14
  include JoyUssdEngine::SessionManager
14
15
 
15
16
  attr_reader :params, :selected_provider
16
- attr_accessor :current_menu, :last_menu
17
+ attr_accessor :current_menu, :last_menu, :expiration
17
18
 
18
- def initialize(params, provider, start_point: nil, end_point: nil )
19
+ def initialize(params, provider, start_point: nil, end_point: nil, expiration: nil )
19
20
 
20
21
  # gets provider currently in use and convert params to match ussd engines params
22
+ @expiration = expiration
21
23
  @selected_provider = provider.new(self)
22
24
  convert_params = @selected_provider.send("request_params",params)
23
25
  @params = convert_params
@@ -25,7 +27,7 @@ module JoyUssdEngine
25
27
  @data = get_state
26
28
  # handles ending or terminating ussd based on provider response (HUBTEL, TWILIO, ETC.)
27
29
  # If a particular provider returns some sort of response that can terminate the app we do that check here
28
- return @current_menu = end_point.to_s if @selected_provider.send("app_terminator", params) || @data[:ClientState] == 'EndJoyUssdEngineiuewhjsdj'
30
+ return @current_menu = end_point.to_s if @selected_provider.send("app_terminator", params) || @data[:ClientState] == 'EndJoyUssdEngine'
29
31
 
30
32
  @current_menu = @data[:ClientState].blank? ? start_point.to_s : @data[:ClientState]
31
33
  end
@@ -43,5 +45,9 @@ module JoyUssdEngine
43
45
  def process
44
46
  load_menu(@current_menu)
45
47
  end
48
+
49
+ def expiration
50
+ @expiration
51
+ end
46
52
  end
47
53
  end
Binary file
Binary file
Binary file
Binary file
@@ -0,0 +1,7 @@
1
+ require "spec_helper"
2
+
3
+ RSpec.describe JoyUssdEngine do
4
+ it "has a version number" do
5
+ expect(JoyUssdEngine::VERSION).not_to be nil
6
+ end
7
+ end
@@ -0,0 +1,26 @@
1
+ require "spec_helper"
2
+
3
+ RSpec.describe JoyUssdEngine::DataTransformer do
4
+
5
+
6
+
7
+ it "saves the context object" do
8
+ expect(@transformer_context).to eq(nil)
9
+ end
10
+
11
+ it "returns false when calling the appterminator method" do
12
+ expect(@transformer_context).to eq(@context)
13
+ end
14
+
15
+ it 'transforms an incoming request params' do
16
+ params = { Message: "hello", phone: "+233578876155" }
17
+ allow(@data_transformer).to receive(:request_params).with(params).and_return({message: params[:Message], session_id: params[:phone]})
18
+ expect(@data_transformer.request_params(params)).to eq({message: "hello", session_id: "+233578876155"})
19
+ end
20
+
21
+ it 'returns false when app_terminator is called' do
22
+ allow(@data_transformer).to receive(:app_terminator).and_call_original
23
+ expect(@data_transformer.app_terminator({})).to eq(false)
24
+ end
25
+
26
+ end
@@ -0,0 +1,12 @@
1
+ require 'bundler/setup'
2
+ Bundler.setup
3
+
4
+ require 'joy_ussd_engine' # and any other gems you need
5
+
6
+ RSpec.configure do |config|
7
+ # some (optional) config here
8
+ @params = { Message: "hello", phone: "+233578876155" }
9
+ @context = JoyUssdEngine::Core
10
+ @data_transformer = JoyUssdEngine::DataTransformer.new(@context)
11
+ @transformer_context = @data_transformer.context
12
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: joy_ussd_engine
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Caleb Mantey
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-03-15 00:00:00.000000000 Z
11
+ date: 2022-04-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: will_paginate
@@ -24,6 +24,34 @@ dependencies:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
26
  version: 3.3.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: rspec
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: redis
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
27
55
  description: A ruby library for building text based applications rapidly. It supports
28
56
  building whatsapp, ussd, telegram and various text or chat applications that communicate
29
57
  with your rails backend. With this library you can target multiple platforms(whatsapp,
@@ -34,12 +62,10 @@ executables: []
34
62
  extensions: []
35
63
  extra_rdoc_files: []
36
64
  files:
37
- - ".DS_Store"
38
- - ".gitignore"
39
- - ".rubocop.yml"
40
65
  - CHANGELOG.md
41
66
  - CODE_OF_CONDUCT.md
42
67
  - Gemfile
68
+ - Gemfile.lock
43
69
  - LICENSE.txt
44
70
  - README.md
45
71
  - Rakefile
@@ -68,10 +94,18 @@ files:
68
94
  - lib/generators/joy_route_menu/templates/joy_route_menu_template.template
69
95
  - lib/joy_ussd_engine.rb
70
96
  - lib/joy_ussd_engine/data_transformer.rb
97
+ - lib/joy_ussd_engine/hubtel_transformer.rb
71
98
  - lib/joy_ussd_engine/menu.rb
72
99
  - lib/joy_ussd_engine/paginate_menu.rb
73
100
  - lib/joy_ussd_engine/session_manager.rb
74
101
  - lib/joy_ussd_engine/version.rb
102
+ - pkg/joy_ussd_engine-0.1.1.gem
103
+ - pkg/joy_ussd_engine-0.1.2.gem
104
+ - pkg/joy_ussd_engine-0.1.3.gem
105
+ - pkg/joy_ussd_engine-0.1.4.gem
106
+ - spec/joy_ussd_engine_spec.rb
107
+ - spec/joy_ussd_engine_transformer.spec
108
+ - spec/spec_helper.rb
75
109
  homepage: https://github.com/Caleb-Mantey/joy_ussd_engine
76
110
  licenses:
77
111
  - MIT
data/.DS_Store DELETED
Binary file
data/.gitignore DELETED
@@ -1,8 +0,0 @@
1
- /.bundle/
2
- /.yardoc
3
- /_yardoc/
4
- /coverage/
5
- /doc/
6
- /pkg/
7
- /spec/reports/
8
- /tmp/
data/.rubocop.yml DELETED
@@ -1,13 +0,0 @@
1
- AllCops:
2
- TargetRubyVersion: 2.4
3
-
4
- Style/StringLiterals:
5
- Enabled: true
6
- EnforcedStyle: double_quotes
7
-
8
- Style/StringLiteralsInInterpolation:
9
- Enabled: true
10
- EnforcedStyle: double_quotes
11
-
12
- Layout/LineLength:
13
- Max: 120