talkable 1.0.2 → 1.0.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (59) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/README.md +132 -60
  4. data/TUTORIAL.md +253 -0
  5. data/lib/talkable/api/origin.rb +0 -1
  6. data/lib/talkable/api/share.rb +28 -6
  7. data/lib/talkable/configuration.rb +1 -2
  8. data/lib/talkable/generators/example_newsletter_signup_generator.rb +22 -0
  9. data/lib/talkable/generators/install_generator.rb +4 -25
  10. data/lib/talkable/generators/invite_standalone_generator.rb +19 -0
  11. data/lib/talkable/generators/shared_generator_methods.rb +15 -0
  12. data/lib/talkable/generators/templates/app/controllers/example_newsletter_signup_controller.rb +17 -0
  13. data/lib/talkable/generators/templates/app/views/example_newsletter_signup/register.html.erb +5 -0
  14. data/lib/talkable/generators/templates/app/views/example_newsletter_signup/register.html.haml +4 -0
  15. data/lib/talkable/generators/templates/app/views/example_newsletter_signup/register.html.slim +4 -0
  16. data/lib/talkable/generators/templates/app/views/example_newsletter_signup/thank_you.html.erb +1 -0
  17. data/lib/talkable/generators/templates/app/views/example_newsletter_signup/thank_you.html.haml +1 -0
  18. data/lib/talkable/generators/templates/app/views/example_newsletter_signup/thank_you.html.slim +1 -0
  19. data/lib/talkable/integration.rb +1 -0
  20. data/lib/talkable/middleware.rb +1 -1
  21. data/lib/talkable/railtie.rb +7 -1
  22. data/lib/talkable/referrals.rb +21 -0
  23. data/lib/talkable/version.rb +1 -1
  24. data/tutorial_images/logo.png +0 -0
  25. data/tutorial_images/navbar.png +0 -0
  26. data/tutorial_images/rails-signup-thankyou-1.png +0 -0
  27. data/tutorial_images/rails-stripe-checkout-1.png +0 -0
  28. data/tutorial_images/talkable-account-settings-more.png +0 -0
  29. data/tutorial_images/talkable-account-settings.png +0 -0
  30. data/tutorial_images/talkable-advocate-friend.png +0 -0
  31. data/tutorial_images/talkable-advocate-share.png +0 -0
  32. data/tutorial_images/talkable-campaign-launch.png +0 -0
  33. data/tutorial_images/talkable-campaign-summary.png +0 -0
  34. data/tutorial_images/talkable-coupon-settings.png +0 -0
  35. data/tutorial_images/talkable-create-account.png +0 -0
  36. data/tutorial_images/talkable-integration-general.png +0 -0
  37. data/tutorial_images/talkable-menu-campaigns.png +0 -0
  38. data/tutorial_images/talkable-menu-dashboard.png +0 -0
  39. data/tutorial_images/talkable-menu-fraud.png +0 -0
  40. data/tutorial_images/talkable-menu-integration.png +0 -0
  41. data/tutorial_images/talkable-menu-launch.png +0 -0
  42. data/tutorial_images/talkable-menu-placement.png +0 -0
  43. data/tutorial_images/talkable-menu-reports.png +0 -0
  44. data/tutorial_images/talkable-menu-site.png +0 -0
  45. data/tutorial_images/talkable-new-campaign-advocate.png +0 -0
  46. data/tutorial_images/talkable-new-campaign-editor.png +0 -0
  47. data/tutorial_images/talkable-new-campaign-incentive-1.png +0 -0
  48. data/tutorial_images/talkable-new-campaign-incentive-2.png +0 -0
  49. data/tutorial_images/talkable-new-campaign-incentive.png +0 -0
  50. data/tutorial_images/talkable-new-campaign-invite.png +0 -0
  51. data/tutorial_images/talkable-new-campaign-save.png +0 -0
  52. data/tutorial_images/talkable-new-campaign-standalone.png +0 -0
  53. data/tutorial_images/talkable-new-campaign.png +0 -0
  54. data/tutorial_images/talkable-new-placement.png +0 -0
  55. data/tutorial_images/talkable-new-site.png +0 -0
  56. data/tutorial_images/talkable-referral-offer.png +0 -0
  57. data/tutorial_images/talkable-siteslug.png +0 -0
  58. data/tutorial_images/talkable-verify-modal.png +0 -0
  59. metadata +49 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c94781742a33ea0f05537d873950e547a8fd44a9557ee5a420dbcf26533ef29b
4
- data.tar.gz: 62d3776c5c8cad349f8c72c78c431d26a655134505ed5e9c06fd310c0527f2a7
3
+ metadata.gz: 85ca74c86aaaea53adef7d531ac81e669245683507ad1077bfa3113e95c48f4c
4
+ data.tar.gz: 1f846f63f097e27030fc5f55deca20674e4eaf6f1a3b6a0b53fde728deddb9d8
5
5
  SHA512:
6
- metadata.gz: 80a4c188451af2bf7c1fcbfb88bfb12673d24900588dc9e72ed541429570fcd6263bf07cf697b45cd6f175699afa951640145c44e9d957cdc67992a528c5ad2f
7
- data.tar.gz: e521fc595017bb499a1dff07f5f2ecbe9b868142f1119daf7e057da48f0972bb839c1d050cc46e2734695415c9d4c3b2e178149d52f49442c04b762c7249c0cb
6
+ metadata.gz: 5a29bbe1f0812a95e3255ae66fc956d76819ff134eb630ea6b0c8005d763d974dbaf21bcb15a08e7990dcb8da604ae35c4b9f5663bbe71b6fffae971817980d5
7
+ data.tar.gz: 9b38ec27c7bba78cb0db4f5cbed0064fc795b00aa3ea7703d3165a4c716d8931157297ea52c7975a2e0b1cf77ee2aec882968b2b40032944edb11df9e97bb456
data/.gitignore CHANGED
@@ -9,6 +9,7 @@
9
9
  /test/tmp/
10
10
  /test/version_tmp/
11
11
  /tmp/
12
+ .idea/
12
13
 
13
14
  # Used by dotenv library to load environment variables.
14
15
  # .env
data/README.md CHANGED
@@ -1,55 +1,58 @@
1
- # Talkable Referral Program API Gem
1
+ # Talkable Referral Marketing API Gem
2
2
  [![](https://ci.solanolabs.com:443/Talkable/talkable-ruby/badges/branches/master?badge_token=c2445aee31992aafe3d8fda62fcde2708f6254f6)](https://ci.solanolabs.com:443/Talkable/talkable-ruby/suites/484176)
3
3
 
4
- Talkable Ruby Gem to make your own referral program in Sinatra or Rails application
4
+ Referral marketing is one of the most powerful strategies for ecommerce sales growth. [Talkable]( https://www.talkable.com) provides a rich platform for referral marketing. You can integrate sophisticated referral marketing into your own ecommerce site using the Talkable Ruby gem for a Rails or Sinatra application.
5
5
 
6
6
  ## Demo
7
7
 
8
- Example of usage at http://github.com/talkable/talkable-spree-example
8
+ See an example application at http://github.com/talkable/talkable-spree-example.
9
9
 
10
- Live demo available at http://spree-example.talkable.com
10
+ See a live demo at http://spree-example.talkable.com.
11
+
12
+ ## Tutorial
13
+
14
+ See a [detailed tutorial](https://railsapps.github.io/talkable-referral-marketing/) by [Learn Ruby on Rails](http://learn-rails.com/learn-ruby-on-rails.html) author Daniel Kehoe.
11
15
 
12
16
  ## Requirements
13
17
 
14
- Gem requires:
15
- - `Ruby` version since `2.3.0`
16
- - `Rack` version since `1.6.1`
18
+ The gem requires:
19
+ - Ruby version 2.3 or newer
20
+ - Rack version 1.6.1 or newer
17
21
 
18
- Gem supports:
19
- - `Ruby on Rails` since `4.0.0`
20
- - `Sinatra` since `1.4.0`
22
+ For integration with:
23
+ - Ruby on Rails 5.0 or newer
24
+ - Sinatra 1.4 or newer
21
25
 
22
- ## Intallation
26
+ ## Gem Installation
23
27
 
24
- ``` ruby
28
+ Add to your project *Gemfile*:
29
+
30
+ ```ruby
25
31
  gem "talkable"
26
32
  ```
27
33
 
28
- ### Step by step instruction
29
-
30
- - [Setup configuration file __*__](#configuration)
31
- - [Add Middleware __*__](#add-talkable-middleware)
32
- - [Load an offer __*__](#load-an-offer)
33
- - [Display a share page __*__](#display-an-offer-inside-view)
34
- - [Integrate Conversion Points](#integrate-conversion-points)
35
- - [Registering a purchase](#registering-a-purchase)
36
- - [Registering other events](#registering-other-events)
34
+ Then run:
37
35
 
38
- __*__ - Automated in Ruby On Rails by using the generator
36
+ ```console
37
+ $ bundle install
38
+ ```
39
39
 
40
- ## Using the Ruby On Rails Generator
40
+ ## Using the Rails Generator
41
41
 
42
- Talkable gem provides Ruby On Rails generator to automate an integration process.
42
+ The Talkable gem provides a Ruby On Rails generator to automate the integration process.
43
43
 
44
- ``` sh
45
- rails generate talkable:install
46
- ```
47
- ``` sh
44
+ ```console
45
+ $ rails generate talkable:install
48
46
  Your Talkable site slug: spree-example
49
47
  Your Talkable API Key: SOME-API-KEY
50
48
  Do you have a custom domain? [Y/n] n
51
49
  ```
52
- ``` sh
50
+
51
+ The Talkable "site slug" is your Account Name (the name of your website, brand or company). You'll also need an API key which you'll find on the Account Settings page when you log in to your account. The generator will ask if you have a custom domain. If your website has a domain like example.com or www.example.com, you can answer, "no." If your website is at shop.example.com, you have a custom domain.
52
+
53
+ The generator adds and modifies several files:
54
+
55
+ ```console
53
56
  create config/initializers/talkable.rb
54
57
  insert app/controllers/application_controller.rb
55
58
  insert app/controllers/application_controller.rb
@@ -61,9 +64,21 @@ Do you have a custom domain? [Y/n] n
61
64
  route get '/invite' => 'invite#show'
62
65
  ```
63
66
 
64
- ## Configuration
67
+ ## Manual Integration
68
+
69
+ Here are the steps that are automated by the Ruby On Rails generator.
65
70
 
66
- ``` ruby
71
+ - [Initializer](#initializer)
72
+ - [Add Middleware](#add-middleware)
73
+ - [Referral Offer](#referral-offer)
74
+ - [Invite Page](#invite-page)
75
+
76
+ ### Initializer
77
+
78
+ You'll need an initializer file to set the Talkable API configuration variables.
79
+
80
+ ```ruby
81
+ ### config/initializers/talkable.rb
67
82
  Talkable.configure do |config|
68
83
  # site slug is taken form ENV["TALKABLE_SITE_SLUG"]
69
84
  config.site_slug = "spree-example"
@@ -79,22 +94,28 @@ Talkable.configure do |config|
79
94
  end
80
95
 
81
96
  ```
97
+ For security, you should set these configuration variables from the Unix environment or use the Rails [encrypted credentials](https://www.engineyard.com/blog/rails-encrypted-credentials-on-rails-5.2) feature so the API key isn't stored in your GitHub repository.
82
98
 
83
- ## Manual Integration
99
+ ### Add Middleware
84
100
 
85
- ### Add Talkable Middleware
101
+ Here's how you can add Talkable middleware manually.
86
102
 
87
- ``` ruby
88
- class Application < Rails::Application
89
- config.middleware.use Talkable::Middleware
90
- end
103
+ _Note that if you're using Devise, it's important to load Talkable middleware before `Warden::Manager`, otherwise you can just go with `app.middleware.use Talkable::Middleware`._
104
+
105
+ ```ruby
106
+ if defined? ::Warden::Manager
107
+ app.middleware.insert_before Warden::Manager, Talkable::Middleware
108
+ else
109
+ app.middleware.use Talkable::Middleware
110
+ end
91
111
  ```
92
112
 
93
- ### Load an offer
113
+ ### Referral Offer
94
114
 
95
- Floating widget at every page
115
+ Add code to retrieve a campaign and display a referral offer on every page of the application.
96
116
 
97
117
  ```ruby
118
+ ### app/controllers/application_controller.rb
98
119
  class ApplicationController < ActionController::Base
99
120
  before_action :load_talkable_offer
100
121
 
@@ -108,9 +129,26 @@ class ApplicationController < ActionController::Base
108
129
  end
109
130
  ```
110
131
 
111
- or invite page at specific path
132
+ ```erb
133
+ ### app/views/shared/_talkable_offer.html.erb
134
+ <%- options ||= {} %>
135
+ <%- if offer %>
136
+ <%== offer.advocate_share_iframe(options) %>
137
+ <% end -%>
138
+ ```
139
+
140
+ ### Invite Page
141
+
142
+ You can add an invite page by implementing an Invite Controller, with route and view files.
143
+
144
+ Add a route to the *config/routes.rb* file:
145
+
146
+ ```ruby
147
+ get '/invite' => 'invite#show'
148
+ ```
112
149
 
113
150
  ```ruby
151
+ ### app/controllers/invite_controller.rb
114
152
  class InviteController < ApplicationController
115
153
  def show
116
154
  # Make sure you have configured Campaign Placements at Talkable site
@@ -122,31 +160,48 @@ class InviteController < ApplicationController
122
160
  end
123
161
  ```
124
162
 
125
- ### Getting information about an offer
163
+ ```erb
164
+ ### app/views/invite/show.html.erb
165
+ <div id="talkable-inline-offer-container"></div>
166
+ <%= render 'shared/talkable_offer',
167
+ offer: @invite_offer,
168
+ options: {iframe: {container: 'talkable-inline-offer-container'}} %>
169
+ ```
170
+
171
+ ## Customize Your Integration
172
+
173
+ ### Getting Information About an Offer
126
174
 
127
175
  ```ruby
128
176
  offer = origin.offer
129
177
  offer.claim_links # => { facebook: "https://www.talkable.com/x/kqiYhR", sms: "https://www.talkable.com/x/PFxhNB" }
130
178
  ```
131
179
 
132
- ### Display an offer inside view
180
+ ### Display an Offer Inside a View
133
181
 
134
- Provide iframe options to show a share page in specific place
182
+ Provide iframe options to show a share page in specific place.
135
183
 
136
184
  ```erb
137
185
  <div id="talkable-inline-offer-container"></div>
138
186
  <%== offer.advocate_share_iframe(iframe: {container: 'talkable-inline-offer-container'}) %>
139
187
  ```
140
188
 
141
- ## Integrate Conversion Points
189
+ ## API Examples
190
+
191
+ See the [API docs](http://docs.talkable.com/api_v2.html) for full details.
192
+
193
+ - [Registering a purchase](#registering-a-purchase)
194
+ - [Registering other events](#registering-other-events)
195
+ - [Working with referrals](#working-with-referrals)
196
+ - [Working with shares](#shares)
197
+ - [More API examples](#more-api-examples)
142
198
 
143
199
  ### Registering a purchase
144
200
 
145
- Registering a purchase has to be implemented manually based on your platform.
146
- > It's highly required to have submitted purchases for closing a referral loop.
201
+ Here's how to register a purchase. We recommend you have submitted purchases for closing a referral loop.
147
202
 
148
203
  ```ruby
149
- Talkable::API::Origin.create(Talkable::API::Origin::PURCHASE, {
204
+ Talkable.register_purchase(
150
205
  email: 'customer@email.com',
151
206
  order_number: 'ORDER-12345',
152
207
  subtotal: 123.45,
@@ -162,13 +217,15 @@ Talkable::API::Origin.create(Talkable::API::Origin::PURCHASE, {
162
217
  title: item.title,
163
218
  }
164
219
  end # optional
165
- })
220
+ )
166
221
  ```
167
222
 
168
223
  ### Registering other events
169
224
 
225
+ Here's how to register other events.
226
+
170
227
  ```ruby
171
- Talkable::API::Origin.create(Talkable::API::Origin::EVENT, {
228
+ Talkable.register_event(
172
229
  email: 'customer@email.com',
173
230
  event_number: 'N12345',
174
231
  event_category: 'user_signuped',
@@ -185,23 +242,38 @@ Talkable::API::Origin.create(Talkable::API::Origin::EVENT, {
185
242
  title: item.title,
186
243
  }
187
244
  end # optional
188
- })
245
+ )
189
246
  ```
190
247
 
191
- ## API
248
+ ### Working with referrals
192
249
 
193
- Full API support according to [DOC](http://docs.talkable.com/api_v2.html)
250
+ ```ruby
251
+ Talkable.approve_referral('test-slug')
252
+ Talkable.void_referral('test-slug')
253
+ Talkable.unblock_referral('test-slug')
254
+ ```
255
+
256
+ ### Shares
257
+
258
+ ```ruby
259
+ Talkable::API::Share.social(short_url_code, channel: Talkable::API::Share::VIA_SMS)
260
+ Talkable::API::Share.direct(short_url_code, channel: Talkable::API::Share::SEND_EMAIL, recipients: 'friend1@example.com,friend2@example.com', subject: 'Hello!', body: 'World!', reminder: false)
261
+ ```
262
+ When sharing via email, the email channel is set to default because for now email is the only channel for direct shares.
263
+
264
+ ### More API examples
265
+
266
+ Here are more API examples.
194
267
 
195
268
  ```ruby
196
- Talkable::API::Origin.create(Talkable::API::Origin::PURCHASE, {
197
- email: 'customer@domain.com',
198
- order_number: '123',
199
- subtotal: 34.56,
200
- })
201
269
  Talkable::API::Offer.find(short_url_code)
202
- Talkable::API::Share.create(short_url_code, Talkable::API::Share::CHANNEL_SMS)
203
- Talkable::API::Reward.find(visitor_uuid: '8fdf75ac-92b4-479d-9974-2f9c64eb2e09')
204
270
  Talkable::API::Person.find(email)
205
271
  Talkable::API::Person.update(email, unsubscribed: true)
206
- Talkable::API::Referral.update(order_number, Talkable::API::Referral::APPROVED)
272
+ Talkable::API::Reward.find(visitor_uuid: '8fdf75ac-92b4-479d-9974-2f9c64eb2e09')
207
273
  ```
274
+
275
+ For more information see the tests.
276
+
277
+ ## Questions? Need Help? Found a bug?
278
+
279
+ If you've got questions about integration, or need any other information, please feel free to [open an issue](https://github.com/talkable/talkable-ruby/issues) so we can reply. Found a bug? Go ahead and submit an [issue](https://github.com/talkable/talkable-ruby/issues).
data/TUTORIAL.md ADDED
@@ -0,0 +1,253 @@
1
+ # Referral Marketing with the Talkable Gem
2
+
3
+ ## Introduction
4
+
5
+ This is the third of three tutorials that introduce referral marketing with [Talkable](https://www.talkable.com/). Referral marketing is a powerful sales driver for SaaS and ecommerce sites. Talkable is a referral marketing platform that supports the complex requirements of the largest and most sophisticated SaaS and ecommerce sites. The [first tutorial](https://railsapps.github.io/talkable-referral-marketing-basics/) introduces basic concepts using the Talkable JavaScript integration library. This tutorial introduces the Talkable API and the Talkable Ruby gem.
6
+
7
+ Read the [first tutorial](https://railsapps.github.io/talkable-referral-marketing-basics/) to grasp the basic concepts you'll need to know to use the Talkable platform. This tutorial shows how to build an application with the same functionality as the first tutorial application, but instead of using the JavaScript integration library you'll use the Ruby gem and the Talkable API.
8
+
9
+ The [second tutorial](https://railsapps.github.io/talkable-referral-marketing-gem/) is an older version of this one before we added an example campaign on account creation and corresponding generators in the gem. The second tutorial has more details about what happens under the hood and could still be interesting for this reason.
10
+
11
+ This third tutorial will mature partly into self-serve users landing page and partly into our documentation in a course of several next releases.
12
+
13
+ ## Requirements
14
+
15
+ You'll need Ruby 2.3+ and Rails 5.0+. You can integrate Talkable in your existing project right away, but a fresh Rails 5 app will also do just fine.
16
+ You'll need only beginner skills as a Rails developer to follow this tutorial.
17
+
18
+ ## Create an Account
19
+
20
+ First you'll need to [set up an account at Talkable](https://admin.talkable.com/register?self_serve).
21
+
22
+ When asked to choose a platform, choose "other" for Rails integration. Then you can provide details such as email address and password.
23
+
24
+ Your site name can be the name of your website, brand or company. You'll be able to add additional sites to your account after you create the first site.
25
+
26
+ For this tutorial, set the Site URL to `http://localhost:3000/` so you can develop and test the application locally.
27
+
28
+ ![talkable-create-account](tutorial_images/talkable-create-account.png)
29
+
30
+ **An example campaign is created automatically for you.**
31
+
32
+ ## Fraud Settings
33
+
34
+ This part of the tutorial won't be needed in a few days as we will be setting different default fraud settings, but it is needed for now.
35
+
36
+ Talkable prevents common (and not-so-common) referral fraud like self-referrals. For example, for product sales sites, referral marketing isn't profitable if shoppers can give themselves discounts by entering their own alternative email addresses.
37
+
38
+ For our tutorial, we'll want to enter our own alternative email addresses and visit the application using the same web browser and the same IP address. We'll change the Fraud Settings to allow this.
39
+
40
+ ![talkable-menu-fraud](tutorial_images/talkable-menu-fraud.png)
41
+
42
+ Set the "Fraud Profiles" (in the upper left corner) to "Low." Then set each individual fraud rule for Advocate and Friend to "Skip," specifically "Matching Email or Cookie on Friend Purchase" and "Matching Cookie on Friend Claim Page." This will allow self-referrals for testing the tutorial application in your local development environment.
43
+
44
+ Be sure to click the "Save Changes" button to save the fraud settings.
45
+
46
+ ## Launch the Campaign
47
+
48
+ You are done with the Talkable administrative interface for now and can launch your example campaign, even though your web application isn't ready yet.
49
+
50
+ Select "Campaigns" from the top navigation bar.
51
+
52
+ ![talkable-menu-campaigns](tutorial_images/talkable-menu-campaigns.png)
53
+
54
+ "Launch" is hidden behind three dots under the "Action" label.
55
+
56
+ ![talkable-menu-launch](tutorial_images/talkable-menu-launch.png)
57
+
58
+ Ignore any warnings, accept the defaults, and click the orange "Launch campaign" button.
59
+
60
+ ![talkable-campaign-launch](tutorial_images/talkable-campaign-launch.png)
61
+
62
+ You'll see a warning, "No Integration Found." Ignore the warning. Talkable checks if an Integration script is present on a production site but our development site is not running or accessible to Talkable. Click the green "Launch Now" button.
63
+
64
+ You campaign will be live and waiting for events triggered by advocates (recommendations) or friends (claiming rewards). All events generated by the Talkable JavaScript integration library will be tracked and captured by Talkable for analysis and optimization. Talkable is designed to gather all data so there is no option to erase or reset campaign data once the campaign is live. However, you can create a new campaign when you are done with experimenting and ready to deploy to production.
65
+
66
+ Next, we will set up the tutorial application to use the Talkable campaign. After that, we can return to the Talkable site to see reports of all activity.
67
+
68
+ ## Install the Talkable Ruby Gem
69
+
70
+ You can use the [Talkable Ruby Gem](https://github.com/talkable/talkable-ruby) to add a referral program to any Ruby application.
71
+
72
+ Add the gem to the project *Gemfile* and run `bundle install`.
73
+
74
+ `gem 'talkable', github: 'talkable/talkable-ruby'`
75
+
76
+ `$ bundle install`
77
+
78
+ Your gem is installed. Let's configure your application.
79
+
80
+ ## Get Your Site Slug and API Key
81
+
82
+ The Talkable gem provides a Ruby On Rails generator for basic configuration of the gem. You'll need to know your "site slug" (a site identifier). The "site slug" can differ from your account name. The "site slug" is the alphanumeric identifier Talkable uses for your website. Your account can support multiple sites and and each has its own "site slug."
83
+
84
+ ### Site Slug
85
+
86
+ You can easily find the "site slug." You'll see the Dashboard page when you log in. The site slug is labelled "Site ID" in the upper left corner.
87
+
88
+ ![talkable-menu-dashboard](tutorial_images/talkable-menu-dashboard.png)
89
+
90
+ ![talkable-siteslug](tutorial_images/talkable-siteslug.png)
91
+
92
+ ### API Key
93
+
94
+ You'll also need an API key. You can find the Account Settings page from the main menu.
95
+
96
+ ![talkable-account-settings](tutorial_images/talkable-account-settings.png)
97
+
98
+ You'll find the API Key under Basic Settings. You won't need the Public API Key (that's just for iOS or Android developers).
99
+
100
+ ![talkable-account-settings-more](tutorial_images/talkable-account-settings-more.png)
101
+
102
+ With your site slug and API key you are ready to run the generator.
103
+
104
+ ## Run the Generator
105
+
106
+ Run the install generator from the terminal:
107
+
108
+ `$ rails generate talkable:install`
109
+
110
+ The generator will prompt you for your "site slug" and API key.
111
+
112
+ The generator will ask if you have a custom domain. You can answer, "no." Custom domains are an advanced feature for Talkable integration.
113
+
114
+ In order for example setup to work you'll need to run two more generators.
115
+
116
+ `$ rails generate talkable:invite_standalone`
117
+
118
+ Invite standalone generator will add a controller and a view where your campaign will be rendered.
119
+ The placement of where to render the campaign is controlled in your campaign settings on Talkable site.
120
+ For the example campaign we chose to use `/invite` as our placement.
121
+
122
+ `$ rails generate talkable:example_newsletter_signup`
123
+
124
+ Example newsletter signup will add a controller, routes and views to your app.
125
+ Our example campaign is configured in a way that will redirect the `friend` to `/example_newsletter_signup` after the claim. On that page a user will be prompted to fake email signup. By default this `signup` only sends a corresponding event via Talkable API.
126
+
127
+ We encourage you to inspect all the stuff produced by generators up close (this section is more detailed in the original ruby gem tutorial).
128
+ Here are a few important details.
129
+
130
+ The file *app/views/invite/show.html.erb* renders the view. It contains a reference to the view partial in the file *shared/talkable_offer*.
131
+
132
+ Your Invite page will display a Talkable referral offer in an iframe, if the following conditions are met:
133
+
134
+ * The Talkable initialization script is in place
135
+ * A Talkable Invite Standalone campaign exists
136
+ * A Talkable Campaign Placement specifies the `http://localhost:3000/invite` URL
137
+ * The Invite page contains `<div id="talkable-offer"></div>`
138
+
139
+ We'll use the Invite page to display a referral offer from the Invite Standalone campaign. You can add links to this page anywhere in your application where you want to encourage visitors to refer a friend.
140
+
141
+ It is important to set a User ID or other unchanging string as an `event_number` each time the page is requested by a visitor. With an unchanging User ID, Talkable will recognize the page requests as repeat visits by the same visitor and will record the visits accordingly.
142
+
143
+ We add the integer 100 to the `current_user.id` to create an `event_number` just for the purpose of this tutorial. If you implemented the tutorial application in our [first tutorial](https://railsapps.github.io/talkable-referral-marketing-basics/), you may have already recorded events for the Invite Standalone campaign. If you build a new application and sign up using event numbers that are already recorded, the Talkable API will respond with an error message "Talkable::API::BadRequest." Adding 100 helps us avoid the issue for anyone who tried the application in the first tutorial.
144
+
145
+ That's all the code we need for our simple refer-a-friend feature. We can try out the tutorial application. But first, let's review the fundamental concept of the *referral loop*.
146
+
147
+ ## Understanding The Referral Loop
148
+
149
+ It's helpful to understand the *referral loop* to grasp the principles of referral marketing. In its most basic form, as we've used it here:
150
+
151
+ * An advocate opens the referral loop by clicking the referral offer on the Invite page and sending a recommendation to a friend.
152
+
153
+ * A friend receives the recommendation and clicks a link on a "claim page"; a cookie is set by Talkable.
154
+
155
+ * The friend gets redirected to the site. On any page they visit, Talkable will recognize the friend's visit by the presence of the cookie.
156
+
157
+ * The friend signs up for an example newsletter (a *conversion event*) and the API request signals Talkable, closing the referral loop.
158
+
159
+ Building on this simple referral loop, you can develop more sophisticated referral marketing campaigns. For example, you can add incentives for the friend or advocate. For a product sales site, you could offer a discount coupon, a rebate, or a non-monetary incentive such as stickers or a t-shirt. You can track an advocate's multiple referrals or even chains of referrals among friends. Talkable supports almost any referral marketing program you can imagine.
160
+
161
+ Now let's try out the tutorial application.
162
+
163
+ ## Time to test how it all works
164
+
165
+ Make sure your campaign is launched. Now go to `http://localhost:3000/invite`
166
+
167
+ ### Advocate Invites a Friend
168
+
169
+ You should now see our campaign page for the Advocate.
170
+
171
+ ![talkable-referral-offer](tutorial_images/talkable-referral-offer.png)
172
+
173
+ Enter your name and email address in the form and click "Invite Friends".
174
+
175
+ The Advocate Share offer will appear on the Invite page.
176
+
177
+ Below you can find the scenario when you share the offer via email. Alternatively you can share via `Copy a Link`. You copy the link and open it in another browser or in the incognito mode. You should be able to claim your reward right away.
178
+
179
+ ![talkable-advocate-share](tutorial_images/talkable-advocate-share.png)
180
+
181
+ Click "Share via Email" and fill in the form with a friend's email address. To try out the application, you should enter your own alternative email address. If you use Gmail, you can use the Gmail "plus" trick to create alternative addresses that will be delivered in your inbox. For example, add a "+foobar" to use "yourname+foobar@gmail.com". This will open a referral loop by registering your email address as an advocate and the friend's email address as a pending invitation.
182
+
183
+ ![talkable-advocate-friend](tutorial_images/talkable-advocate-friend.png)
184
+
185
+ You are done with the Invite page. Sign out of the application so you can pretend to be the friend next time you visit the web application.
186
+
187
+ ### Friend Receives Email
188
+
189
+ Check your email inbox. Less than a minute after submitting the form with the friend email address, you should receive an email message from Talkable. There will be a prominent "Claim Your Gift" button in the message body. The claim button is a link to the Talkable server that will track the action and redirect to your web application.
190
+
191
+ ### Friend Clicks Link
192
+
193
+ Click the "Claim Your Gift" button in the email message and you will visit the newsletter signup page of your application.
194
+
195
+ ### Friend Signs Up
196
+
197
+ Now sign up for an example newsletter.
198
+
199
+ You'll see the Thank You page. Behind the scenes, the Talkable controller will make an API request to Talkable to record the signup event. This closes a referral loop because Talkable recognizes the friend's email address from the advocate's invitation.
200
+
201
+ Next let's view reports on the Talkable site and see the events that Talkable has recorded.
202
+
203
+ ## Talkable Reports
204
+
205
+ Sign in to the Talkable site and select "Reports" from the top navigation bar.
206
+
207
+ ![talkable-menu-reports](tutorial_images/talkable-menu-reports.png)
208
+
209
+ View some of the reports under the "Support" heading.
210
+
211
+ The **People** report will show the email addresses of the advocates and friends.
212
+
213
+ The **Events** report will show the signup activities.
214
+
215
+ The **Advocate Offers** report will show the email address of each advocate and the name of the campaign to which each responded.
216
+
217
+ The **Offer Shares** report will show the linked advocates and friends.
218
+
219
+ The **Referrals** report will show the complete details of each referral event. If any event indicates "Campaign doesn't offer Self-Referral Incentive," you will need to check the Fraud Settings from the main menu. You can adjust the Fraud Settings to allow self-referrals for the purpose of testing.
220
+
221
+ Take a few minutes to view the reports and see how the referral activity is logged for analysis and action. If Talkable was integrated correctly in your application, all the referral activity should be reflected in the reports.
222
+
223
+ ## Campaign Performance
224
+
225
+ Finally, to see a high-level analysis of your referral marketing campaign, find the Campaign Performance summary. Select "Campaigns" from the top navigation bar.
226
+
227
+ ![talkable-menu-campaigns](tutorial_images/talkable-menu-campaigns.png)
228
+
229
+ Click the "Invite Standalone" campaign to see a campaign summary.
230
+
231
+ ![talkable-campaign-summary](tutorial_images/talkable-campaign-summary.png)
232
+
233
+ The Campaign Performance summary shows the funnel of impressions (page visits), advocates (sharers), sharing events, and visits by friends. The Campaign Performance isn't the end point of a typical referral marketing campaign. It is the beginning point for further testing and optimization to refine a referral marketing campaign.
234
+
235
+ With this introduction to referral marketing and the Talkable platform, you've seen the basics of referral marketing.
236
+
237
+ The functionality of the tutorial application is minimal but you’ve learned enough to add simple referral marketing to any Rails application.
238
+
239
+ ## Troubleshooting
240
+
241
+ Troubleshooting options are limited with the Ruby gem.
242
+
243
+ ### Verification Query
244
+
245
+ In the [first tutorial](https://railsapps.github.io/talkable-referral-marketing-basics/), we were able to use a verification query to check that the Talkable initialization script is connecting to the Talkable server and recognizing your account. For example:
246
+
247
+ * [http://localhost:3000/?tkbl_verify_integration=true](http://localhost:3000/?tkbl_verify_integration=true)
248
+
249
+ However, this verification query doesn't work with the Ruby gem.
250
+
251
+ ## Support
252
+
253
+ Was this tutorial helpful? For questions or more information, contact <a href="mailto:support@talkable.com">support@talkable.com</a>.
@@ -19,7 +19,6 @@ module Talkable
19
19
 
20
20
  def default_data
21
21
  {
22
- uuid: Talkable.visitor_uuid,
23
22
  traffic_source: DEFAULT_TRAFFIC_SOURCE,
24
23
  }
25
24
  end
@@ -1,18 +1,40 @@
1
+ # More at http://docs.talkable.com/api_v2/shares.html
1
2
  module Talkable
2
3
  module API
3
4
  class Share < Base
4
- CHANNEL_FACEBOOK = "facebook".freeze
5
- CHANNEL_FACEBOOK_MESSAGE = "facebook_message".freeze
6
- CHANNEL_TWITTER = "twitter".freeze
7
- CHANNEL_SMS = "sms".freeze
8
- CHANNEL_OTHER = "other".freeze
5
+ # social
6
+ VIA_FACEBOOK = "facebook"
7
+ VIA_FB_MESSAGE = "facebook_message"
8
+ VIA_TWITTER = "twitter"
9
+ VIA_LINKEDIN = "linkedin"
10
+ VIA_WHATSAPP = "twitter"
11
+ VIA_SMS = "sms"
12
+ VIA_OTHER = "other"
13
+
14
+ # direct
15
+ SEND_EMAIL = "email"
9
16
 
10
17
  class << self
11
18
  def create(short_url_code, channel)
12
- post "/offers/#{short_url_code}/shares", {
19
+ warn "[DEPRECATION] `create` is deprecated. Please use `social` or `direct` instead."
20
+ social(short_url_code, channel: channel)
21
+ end
22
+
23
+ def social(short_url_code, channel:)
24
+ post "/offers/#{short_url_code}/shares/social", {
13
25
  channel: channel,
14
26
  }
15
27
  end
28
+
29
+ def direct(short_url_code, channel: SEND_EMAIL, recipients:, subject: nil, body: nil, reminder: nil)
30
+ raise ArgumentError, 'Email is the only supported sharing channel' unless channel == SEND_EMAIL
31
+ post "/offers/#{short_url_code}/shares/#{channel}", {
32
+ recipients: recipients,
33
+ subject: subject,
34
+ body: body,
35
+ reminder: reminder,
36
+ }
37
+ end
16
38
  end
17
39
  end
18
40
  end
@@ -13,7 +13,7 @@ module Talkable
13
13
  def initialize
14
14
  self.site_slug = ENV["TALKABLE_SITE_SLUG"]
15
15
  self.api_key = ENV["TALKABLE_API_KEY"]
16
- self.server = "https://www.talkable.com"
16
+ self.server = DEFAULT_SERVER
17
17
  end
18
18
 
19
19
  def apply(config)
@@ -35,6 +35,5 @@ module Talkable
35
35
  def default_js_integration_library
36
36
  "//d2jjzw81hqbuqv.cloudfront.net/integration/clients/#{site_slug}.min.js"
37
37
  end
38
-
39
38
  end
40
39
  end
@@ -0,0 +1,22 @@
1
+ require 'talkable/generators/shared_generator_methods'
2
+
3
+ module Talkable
4
+ class ExampleNewsletterSignupGenerator < Rails::Generators::Base
5
+ include Talkable::SharedGeneratorMethods
6
+
7
+ source_root File.expand_path("../templates", __FILE__)
8
+ class_option :haml, type: :boolean, default: false
9
+ class_option :slim, type: :boolean, default: false
10
+
11
+ def add_invite_controller
12
+ ext = template_lang
13
+
14
+ copy_file "app/controllers/example_newsletter_signup_controller.rb", "app/controllers/example_newsletter_signup_controller.rb"
15
+ copy_file "app/views/example_newsletter_signup/register.html.#{ext}", "app/views/example_newsletter_signup/register.html.#{ext}"
16
+ copy_file "app/views/example_newsletter_signup/thank_you.html.#{ext}", "app/views/example_newsletter_signup/thank_you.html.#{ext}"
17
+ route "get '/example_newsletter_signup/thank_you' => 'example_newsletter_signup#thank_you'"
18
+ route "get '/example_newsletter_signup' => 'example_newsletter_signup#register'"
19
+ route "post '/example_newsletter_signup' => 'example_newsletter_signup#submit'"
20
+ end
21
+ end
22
+ end
@@ -1,5 +1,9 @@
1
+ require 'talkable/generators/shared_generator_methods'
2
+
1
3
  module Talkable
2
4
  class InstallGenerator < Rails::Generators::Base
5
+ include Talkable::SharedGeneratorMethods
6
+
3
7
  source_root File.expand_path("../templates", __FILE__)
4
8
  class_option :haml, type: :boolean, default: false
5
9
  class_option :slim, type: :boolean, default: false
@@ -52,30 +56,5 @@ RUBY
52
56
  end
53
57
  end
54
58
  end
55
-
56
- def add_invite_controller
57
- ext = template_lang
58
-
59
- copy_file "app/controllers/invite_controller.rb", "app/controllers/invite_controller.rb"
60
- copy_file "app/views/invite/show.html.#{ext}", "app/views/invite/show.html.#{ext}"
61
- route "get '/invite' => 'invite#show'"
62
- end
63
-
64
- protected
65
-
66
- def template_lang
67
- @template_lang ||= if options[:haml]
68
- 'haml'
69
- elsif options[:slim]
70
- 'slim'
71
- else
72
- Rails::Generators.options[:rails][:template_engine].to_s.downcase
73
- end
74
- end
75
-
76
- def erb?
77
- template_lang == 'erb'
78
- end
79
-
80
59
  end
81
60
  end
@@ -0,0 +1,19 @@
1
+ require 'talkable/generators/shared_generator_methods'
2
+
3
+ module Talkable
4
+ class InviteStandaloneGenerator < Rails::Generators::Base
5
+ include Talkable::SharedGeneratorMethods
6
+
7
+ source_root File.expand_path("../templates", __FILE__)
8
+ class_option :haml, type: :boolean, default: false
9
+ class_option :slim, type: :boolean, default: false
10
+
11
+ def add_invite_controller
12
+ ext = template_lang
13
+
14
+ copy_file "app/controllers/invite_controller.rb", "app/controllers/invite_controller.rb"
15
+ copy_file "app/views/invite/show.html.#{ext}", "app/views/invite/show.html.#{ext}"
16
+ route "get '/invite' => 'invite#show'"
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,15 @@
1
+ module Talkable::SharedGeneratorMethods
2
+ def template_lang
3
+ @template_lang ||= if options[:haml]
4
+ 'haml'
5
+ elsif options[:slim]
6
+ 'slim'
7
+ else
8
+ Rails::Generators.options[:rails][:template_engine].to_s.downcase
9
+ end
10
+ end
11
+
12
+ def erb?
13
+ template_lang == 'erb'
14
+ end
15
+ end
@@ -0,0 +1,17 @@
1
+ class ExampleNewsletterSignupController < ApplicationController
2
+ def register
3
+ end
4
+
5
+ def submit
6
+ Talkable.register_event(
7
+ email: params[:email],
8
+ event_number: Digest::MD5.hexdigest(params[:email]),
9
+ event_category: 'example_newsletter_signup'
10
+ )
11
+
12
+ redirect_to example_newsletter_signup_thank_you_path
13
+ end
14
+
15
+ def thank_you
16
+ end
17
+ end
@@ -0,0 +1,5 @@
1
+ <h3>Example newsletter signup (this form will just submit an event to talkable)</h3>
2
+ <%= form_tag :example_newsletter_signup do %>
3
+ <%= email_field_tag :email %>
4
+ <%= submit_tag 'SignUp' %>
5
+ <% end %>
@@ -0,0 +1,4 @@
1
+ %h3 Example newsletter signup (this form will just submit an event to talkable)
2
+ = form_tag :example_newsletter_signup do
3
+ = email_field_tag :email
4
+ = submit_tag 'SignUp'
@@ -0,0 +1,4 @@
1
+ h3 Example newsletter signup (this form will just submit an event to talkable)
2
+ = form_tag :example_newsletter_signup do
3
+ = email_field_tag :email
4
+ = submit_tag 'SignUp'
@@ -0,0 +1 @@
1
+ <h3>You should now have a closed referral loop, see talkable admin for more details</h3>
@@ -0,0 +1 @@
1
+ %h3 You should now have a closed referral loop, see talkable admin for more details
@@ -0,0 +1 @@
1
+ h3 You should now have a closed referral loop, see talkable admin for more details
@@ -23,6 +23,7 @@ module Talkable
23
23
 
24
24
  def default_params
25
25
  {
26
+ uuid: Talkable.visitor_uuid,
26
27
  r: Talkable.current_url,
27
28
  }
28
29
  end
@@ -73,7 +73,7 @@ module Talkable
73
73
  end
74
74
 
75
75
  def sync_uuid_url(uuid)
76
- Furi.update("https://www.talkable.com/public/1x1.gif", query: {current_visitor_uuid: uuid})
76
+ Furi.update("#{Talkable.configuration.server}/public/1x1.gif", query: {current_visitor_uuid: uuid})
77
77
  end
78
78
 
79
79
  def sync_uuid_content(uuid)
@@ -2,10 +2,16 @@ module Talkable
2
2
  class Railtie < Rails::Railtie
3
3
  generators do
4
4
  require 'talkable/generators/install_generator'
5
+ require 'talkable/generators/invite_standalone_generator'
6
+ require 'talkable/generators/example_newsletter_signup_generator'
5
7
  end
6
8
 
7
9
  initializer "talkable.add_middleware" do |app|
8
- app.middleware.use Talkable::Middleware
10
+ if defined? ::Warden::Manager
11
+ app.middleware.insert_before Warden::Manager, Talkable::Middleware
12
+ else
13
+ app.middleware.use Talkable::Middleware
14
+ end
9
15
  end
10
16
  end
11
17
  end
@@ -0,0 +1,21 @@
1
+ module Talkable
2
+ class << self
3
+ def approve_referral(origin_slug)
4
+ update_referral(origin_slug, Talkable::API::Referral::APPROVED)
5
+ end
6
+
7
+ def void_referral(origin_slug)
8
+ update_referral(origin_slug, Talkable::API::Referral::VOIDED)
9
+ end
10
+
11
+ def unblock_referral(origin_slug)
12
+ update_referral(origin_slug, Talkable::API::Referral::UNBLOCKED)
13
+ end
14
+
15
+ private
16
+
17
+ def update_referral(origin_slug, status)
18
+ Talkable::API::Referral.update(origin_slug, status)
19
+ end
20
+ end
21
+ end
@@ -1,3 +1,3 @@
1
1
  module Talkable
2
- VERSION = "1.0.2"
2
+ VERSION = "1.0.3"
3
3
  end
Binary file
Binary file
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: talkable
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.2
4
+ version: 1.0.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Talkable
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-04-19 00:00:00.000000000 Z
11
+ date: 2018-07-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rack
@@ -163,6 +163,7 @@ files:
163
163
  - LICENSE
164
164
  - README.md
165
165
  - Rakefile
166
+ - TUTORIAL.md
166
167
  - lib/talkable.rb
167
168
  - lib/talkable/api.rb
168
169
  - lib/talkable/api/base.rb
@@ -174,8 +175,18 @@ files:
174
175
  - lib/talkable/api/share.rb
175
176
  - lib/talkable/api/visitor.rb
176
177
  - lib/talkable/configuration.rb
178
+ - lib/talkable/generators/example_newsletter_signup_generator.rb
177
179
  - lib/talkable/generators/install_generator.rb
180
+ - lib/talkable/generators/invite_standalone_generator.rb
181
+ - lib/talkable/generators/shared_generator_methods.rb
182
+ - lib/talkable/generators/templates/app/controllers/example_newsletter_signup_controller.rb
178
183
  - lib/talkable/generators/templates/app/controllers/invite_controller.rb
184
+ - lib/talkable/generators/templates/app/views/example_newsletter_signup/register.html.erb
185
+ - lib/talkable/generators/templates/app/views/example_newsletter_signup/register.html.haml
186
+ - lib/talkable/generators/templates/app/views/example_newsletter_signup/register.html.slim
187
+ - lib/talkable/generators/templates/app/views/example_newsletter_signup/thank_you.html.erb
188
+ - lib/talkable/generators/templates/app/views/example_newsletter_signup/thank_you.html.haml
189
+ - lib/talkable/generators/templates/app/views/example_newsletter_signup/thank_you.html.slim
179
190
  - lib/talkable/generators/templates/app/views/invite/show.html.erb
180
191
  - lib/talkable/generators/templates/app/views/invite/show.html.haml
181
192
  - lib/talkable/generators/templates/app/views/invite/show.html.slim
@@ -186,12 +197,48 @@ files:
186
197
  - lib/talkable/integration.rb
187
198
  - lib/talkable/middleware.rb
188
199
  - lib/talkable/railtie.rb
200
+ - lib/talkable/referrals.rb
189
201
  - lib/talkable/resources.rb
190
202
  - lib/talkable/resources/offer.rb
191
203
  - lib/talkable/resources/origin.rb
192
204
  - lib/talkable/version.rb
193
205
  - solano.yml
194
206
  - talkable.gemspec
207
+ - tutorial_images/logo.png
208
+ - tutorial_images/navbar.png
209
+ - tutorial_images/rails-signup-thankyou-1.png
210
+ - tutorial_images/rails-stripe-checkout-1.png
211
+ - tutorial_images/talkable-account-settings-more.png
212
+ - tutorial_images/talkable-account-settings.png
213
+ - tutorial_images/talkable-advocate-friend.png
214
+ - tutorial_images/talkable-advocate-share.png
215
+ - tutorial_images/talkable-campaign-launch.png
216
+ - tutorial_images/talkable-campaign-summary.png
217
+ - tutorial_images/talkable-coupon-settings.png
218
+ - tutorial_images/talkable-create-account.png
219
+ - tutorial_images/talkable-integration-general.png
220
+ - tutorial_images/talkable-menu-campaigns.png
221
+ - tutorial_images/talkable-menu-dashboard.png
222
+ - tutorial_images/talkable-menu-fraud.png
223
+ - tutorial_images/talkable-menu-integration.png
224
+ - tutorial_images/talkable-menu-launch.png
225
+ - tutorial_images/talkable-menu-placement.png
226
+ - tutorial_images/talkable-menu-reports.png
227
+ - tutorial_images/talkable-menu-site.png
228
+ - tutorial_images/talkable-new-campaign-advocate.png
229
+ - tutorial_images/talkable-new-campaign-editor.png
230
+ - tutorial_images/talkable-new-campaign-incentive-1.png
231
+ - tutorial_images/talkable-new-campaign-incentive-2.png
232
+ - tutorial_images/talkable-new-campaign-incentive.png
233
+ - tutorial_images/talkable-new-campaign-invite.png
234
+ - tutorial_images/talkable-new-campaign-save.png
235
+ - tutorial_images/talkable-new-campaign-standalone.png
236
+ - tutorial_images/talkable-new-campaign.png
237
+ - tutorial_images/talkable-new-placement.png
238
+ - tutorial_images/talkable-new-site.png
239
+ - tutorial_images/talkable-referral-offer.png
240
+ - tutorial_images/talkable-siteslug.png
241
+ - tutorial_images/talkable-verify-modal.png
195
242
  homepage: https://github.com/talkable/talkable-ruby
196
243
  licenses:
197
244
  - MIT