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 +4 -4
- data/CHANGELOG.md +9 -1
- data/Gemfile.lock +59 -0
- data/README.md +181 -70
- data/images/lifecycle.jpg +0 -0
- data/joy_ussd_engine.gemspec +9 -2
- data/lib/joy_ussd_engine/data_transformer.rb +2 -1
- data/lib/joy_ussd_engine/hubtel_transformer.rb +35 -0
- data/lib/joy_ussd_engine/menu.rb +8 -8
- data/lib/joy_ussd_engine/paginate_menu.rb +2 -2
- data/lib/joy_ussd_engine/version.rb +1 -1
- data/lib/joy_ussd_engine.rb +9 -3
- data/pkg/joy_ussd_engine-0.1.1.gem +0 -0
- data/pkg/joy_ussd_engine-0.1.2.gem +0 -0
- data/pkg/joy_ussd_engine-0.1.3.gem +0 -0
- data/pkg/joy_ussd_engine-0.1.4.gem +0 -0
- data/spec/joy_ussd_engine_spec.rb +7 -0
- data/spec/joy_ussd_engine_transformer.spec +26 -0
- data/spec/spec_helper.rb +12 -0
- metadata +39 -5
- data/.DS_Store +0 -0
- data/.gitignore +0 -8
- data/.rubocop.yml +0 -13
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 80f97294b96fb0c861b5d65226138e0f3b16e86fb71ced93371b62c2a22537b5
|
4
|
+
data.tar.gz: d28f4cfbac9b181baa531ecee3256d243b4a8c2ca09d893f7f60e55ca4221dd8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d72616d570bea38872ec4d7c6c0294aa89891e43637735f87750d8f94ee66e94b999a14ecb690b18e0b0bcb3959e740077ee8b245c7b6a57640099f61f50893e
|
7
|
+
data.tar.gz: c22582c624b22a21d6a4e711ff72ef398b5a0bc693c6125a11ec4b04a121bee943faa6926aac714129fe440c1c5700c2c42532a737701af992f740185522ef10
|
data/CHANGELOG.md
CHANGED
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
|
103
|
-
|
|
104
|
-
| params
|
105
|
-
| [data_transformer](#
|
106
|
-
| [start_point](#menu)
|
107
|
-
| [end_point](#menu)
|
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
|
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
|
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
|
191
|
-
|
|
192
|
-
| context | object
|
193
|
-
| field_name\* | string
|
194
|
-
| menu_text\* | string
|
195
|
-
| error_text | string
|
196
|
-
| skip_save | boolean
|
197
|
-
| menu_items | array <{title: '',
|
198
|
-
| field_error | boolean
|
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
|
215
|
-
| joy_release | none | This method renders
|
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
|
223
|
-
| get_selected_item | Gets the
|
224
|
-
| raise_error | Takes an error message as an arguments and
|
225
|
-
| has_selected? | Checks if the user has selected an item
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
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 <
|
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
|
data/joy_ussd_engine.gemspec
CHANGED
@@ -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
|
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
|
data/lib/joy_ussd_engine/menu.rb
CHANGED
@@ -31,7 +31,7 @@ module JoyUssdEngine
|
|
31
31
|
def joy_release(error_message = "")
|
32
32
|
@context.reset_state
|
33
33
|
{
|
34
|
-
ClientState: "
|
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.
|
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] != "
|
160
|
-
@context.reset_state if response[:ClientState] == "
|
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] != "
|
197
|
-
@context.reset_state if response[:ClientState] == "
|
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] != "
|
214
|
-
@context.reset_state if response[:ClientState] == "
|
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] != "
|
189
|
-
@context.reset_state if response[:ClientState] == "
|
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
|
data/lib/joy_ussd_engine.rb
CHANGED
@@ -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] == '
|
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,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
|
data/spec/spec_helper.rb
ADDED
@@ -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.
|
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-
|
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
data/.rubocop.yml
DELETED