bg-common 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (36) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +9 -0
  3. data/.ruby-version +1 -0
  4. data/.travis.yml +5 -0
  5. data/CODE_OF_CONDUCT.md +74 -0
  6. data/Gemfile +4 -0
  7. data/LICENSE +674 -0
  8. data/README.md +274 -0
  9. data/Rakefile +10 -0
  10. data/app/jobs/bg/common/analytics/ga_event_job.rb +13 -0
  11. data/app/jobs/bg/common/analytics/intercom_create_user_job.rb +15 -0
  12. data/app/jobs/bg/common/analytics/intercom_event_job.rb +13 -0
  13. data/app/jobs/bg/common/analytics/intercom_export_users_job.rb +15 -0
  14. data/app/jobs/bg/common/analytics/intercom_update_users_job.rb +17 -0
  15. data/app/jobs/bg/common/analytics/keen_event_job.rb +13 -0
  16. data/bg-common.gemspec +26 -0
  17. data/bin/console +14 -0
  18. data/bin/setup +8 -0
  19. data/lib/bg/common/analytics/ga.rb +12 -0
  20. data/lib/bg/common/analytics/google_analytics/client.rb +27 -0
  21. data/lib/bg/common/analytics/google_analytics/events.rb +40 -0
  22. data/lib/bg/common/analytics/intercom/client.rb +39 -0
  23. data/lib/bg/common/analytics/intercom/events.rb +36 -0
  24. data/lib/bg/common/analytics/intercom/user.rb +67 -0
  25. data/lib/bg/common/analytics/intercom.rb +14 -0
  26. data/lib/bg/common/analytics/keen/client.rb +16 -0
  27. data/lib/bg/common/analytics/keen/events.rb +31 -0
  28. data/lib/bg/common/analytics/keen.rb +12 -0
  29. data/lib/bg/common/analytics/tracker.rb +24 -0
  30. data/lib/bg/common/analytics.rb +43 -0
  31. data/lib/bg/common/engine.rb +8 -0
  32. data/lib/bg/common/middlewares/basic_auth.rb +32 -0
  33. data/lib/bg/common/rails.rb +9 -0
  34. data/lib/bg/common/version.rb +5 -0
  35. data/lib/bg/common.rb +50 -0
  36. metadata +119 -0
data/README.md ADDED
@@ -0,0 +1,274 @@
1
+ # BG::Common
2
+
3
+ A Ruby library of common behaviors, especially for Ruby on Rails applications.
4
+
5
+ # Table of Contents
6
+
7
+ <!-- MarkdownTOC depth=4 autolink=true bracket=round -->
8
+
9
+ - [Installation](#installation)
10
+ - [Usage](#usage)
11
+ - [I. Basic Auth](#i-basic-auth)
12
+ - [II. Analytics](#ii-analytics)
13
+ - [Google Analytics](#google-analytics)
14
+ - [Intercom](#intercom)
15
+ - [Keen.io](#keenio)
16
+ - [Development](#development)
17
+ - [Contributing](#contributing)
18
+
19
+ <!-- /MarkdownTOC -->
20
+
21
+ ## Installation
22
+
23
+ Add this line to your application's Gemfile:
24
+
25
+ ```ruby
26
+ gem 'bg-common'
27
+ ```
28
+
29
+ And then execute:
30
+
31
+ $ bundle
32
+
33
+ Or install it yourself as:
34
+
35
+ $ gem install bg-common
36
+
37
+ ## Usage
38
+
39
+ ### I. Basic Auth
40
+
41
+ To hide an app from bots and people who are trying to troll us, we want to hide the app behind a "firewall". Typically we want to do that for staging or any other environment that is not public.
42
+
43
+ The best way to do that is to put the app behind Basic Auth.
44
+
45
+ To enable Basic Auth, add the following as an ENV variable:
46
+
47
+ ```
48
+ BASIC_AUTH_ENABLED=true
49
+ BASIC_AUTH_USERNAME=user
50
+ BASIC_AUTH_PASSWORD=password
51
+ ```
52
+
53
+ make sure to change `user` and `password` to something a bit more secure!
54
+
55
+ ### II. Analytics
56
+
57
+ This library provides a common interface for sending events to different analytics/user tracking platforms (eg. Google Analytics, Intercom, Keen.io).
58
+
59
+ **Caveat:** Although the interface is the same, the data sent to each one is different. Some accept custom attributes, and others don't. So for that reason, you will still need to have knowledge of what each platform accepts as params. Those are explained below for each one of them.
60
+
61
+ To track an event on all of the supported platforms, all you need to do is call the `Tracker` class, with the proper params.
62
+
63
+ ```ruby
64
+ require 'bg/common/analytics'
65
+
66
+ user = current_user # or whatever the logged in user object is in your case.
67
+
68
+ # Track on the various analytics tools:
69
+ tracker_data = {
70
+ ga: {
71
+ category: 'Users',
72
+ action: 'User Logged In',
73
+ label: user.email,
74
+ value: nil,
75
+ bounce: false,
76
+ },
77
+ intercom: {
78
+ event_name: 'user-logged-in',
79
+ created_at: Time.now.to_i,
80
+ email: user.email,
81
+ },
82
+ keen: {
83
+ event: 'user_logged_in',
84
+ data: {
85
+ user_id: user.id,
86
+ email: user.email,
87
+ date: Time.now.utc.to_i, # Cannot be a time object, so cast as
88
+ # Integer or String because of the following:
89
+ # ActiveJob::SerializationError
90
+ # (Unsupported argument type: Time)
91
+ }
92
+ }
93
+ }
94
+
95
+ BG::Common::Analytics::Tracker.new.call(tracker_data)
96
+ ```
97
+
98
+ The call above detects whether you have background jobs enabled by checking if the `ActiveJob::Base` class is defined. If it is, it will execute the calls to each platform by creating a background job for each one. This is beneficial to make sure that these non critical third party calls do not block the user action.
99
+
100
+ If `ActiveJob::Base` is not defined, the calls would be perform inline.
101
+
102
+ **What if I do not want Google to creep on my users???**
103
+
104
+ Well that's easy, do not enable it. For each supported platform, the gem determines if it's enabled by checking on a couple of things (see the sections below for more detail) and if the checks pass, then that platform is included in the tracker call. Otherwise, that platform is ignored.
105
+
106
+ **Oh boy! That's a long namespace!**
107
+
108
+ `BG::Common::Analytics::Tracker` is a lot to type, but we expect that you will setup your class that will inherit from this one, and create your own methods that determine how the data is structured for each event. Something like this:
109
+
110
+ ```ruby
111
+ class MyAnalytics < BG::Common::Analytics::Tracker
112
+ def track_login user
113
+ tracker_data = {
114
+ ga: {
115
+ category: 'Users',
116
+ action: 'User Logged In',
117
+ label: user.email,
118
+ value: nil,
119
+ bounce: false,
120
+ },
121
+ intercom: {
122
+ event_name: 'user-logged-in',
123
+ created_at: Time.now.to_i,
124
+ email: user.email,
125
+ },
126
+ keen: {
127
+ event: 'user_logged_in',
128
+ data: {
129
+ user_id: user.id,
130
+ email: user.email,
131
+ date: Time.now.utc.to_i,
132
+ }
133
+ }
134
+ }
135
+
136
+ call tracker_data
137
+ end
138
+ end
139
+
140
+ MyAnalytics.new.track_login user
141
+ ```
142
+
143
+ or if you want to build something more extendable:
144
+
145
+ ```
146
+ # lib/my_app/analytics/base.rb
147
+ module MyApp
148
+ module Analytics
149
+ class Base < ::BG::Common::Analytics::Tracker
150
+ def initialize user:, additional_data:
151
+ super()
152
+
153
+ @user = user
154
+ @additional_data = additional_data
155
+ end
156
+
157
+ def call
158
+ default_keen_data = extract_default_keen_data user: @user, additional_data: @additional_data
159
+ extra_keen_data = extract_extra_keen_data additional_data: @additional_data
160
+
161
+ tracker_data = {
162
+ ga: { ... },
163
+ intercom: { ... },
164
+ keen: {
165
+ event: event_name,
166
+ data: default_keen_data.merge(extra_keen_data),
167
+ },
168
+ }
169
+
170
+ super tracker_data
171
+ end
172
+
173
+ private
174
+
175
+ def extract_extra_keen_data additional_data:
176
+ raise NotImplementedError
177
+ end
178
+
179
+ def event_name
180
+ raise NotImplementedError
181
+ end
182
+
183
+ def extract_default_keen_data user:, additional_data:
184
+ {
185
+ user_id: user.id,
186
+ user_email: user.email,
187
+ datestamp: Time.now.utc.to_i,
188
+ }
189
+ end
190
+ end
191
+ end
192
+ end
193
+
194
+ # lib/my_app/analytics/comment.rb
195
+ module MyApp
196
+ module Analytics
197
+ module Comment
198
+ class Create < Base
199
+ private
200
+
201
+ def extract_extra_keen_data additional_data:
202
+ {
203
+ comment_id: additional_data[:comment_id]
204
+ }
205
+ end
206
+
207
+ def event_name
208
+ 'new_comment'
209
+ end
210
+ end
211
+ end
212
+ end
213
+ end
214
+
215
+ # in comments_controller.rb
216
+ MyApp::Analytics::Comment::Create.new(user: current_user, additional_data: {
217
+ comment_id: @comment.id,
218
+ }).call
219
+ ```
220
+
221
+ #### Google Analytics
222
+
223
+ All you need to have GA support is to add the `gabba` gem to your `Gemfile`, and the following `ENV` variables:
224
+
225
+ ```ruby
226
+ # Gemfile
227
+ gem 'gabba'
228
+ ```
229
+
230
+ ```
231
+ # .env
232
+ GA_TRACKER_CODE=...
233
+ GA_DOMAIN=...
234
+ ```
235
+
236
+ which are required to configure the GA client.
237
+
238
+ #### Intercom
239
+
240
+ All you need to have Intercom support is to include the `intercom` gem:
241
+
242
+ ```ruby
243
+ # Gemfile
244
+ gem 'intercom'
245
+ ```
246
+
247
+ **PS:** make sure you configure the gem in the initializer as specified by the Intercom docs.
248
+
249
+ #### Keen.io
250
+
251
+ All you need to have GA support is to add the `keen` gem to your `Gemfile`, and the following `ENV` variables:
252
+
253
+ ```ruby
254
+ # Gemfile
255
+ gem 'keen'
256
+ ```
257
+
258
+ ```
259
+ # .env
260
+ KEEN_PROJECT_ID=...
261
+ ```
262
+
263
+ which are required to configure the Keen client.
264
+
265
+ ## Development
266
+
267
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
268
+
269
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
270
+
271
+ ## Contributing
272
+
273
+ Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/bg-common. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
274
+
data/Rakefile ADDED
@@ -0,0 +1,10 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+
4
+ Rake::TestTask.new(:test) do |t|
5
+ t.libs << "test"
6
+ t.libs << "lib"
7
+ t.test_files = FileList['test/**/*_test.rb']
8
+ end
9
+
10
+ task :default => :test
@@ -0,0 +1,13 @@
1
+ module BG
2
+ module Common
3
+ module Analytics
4
+ class GAEventJob < ::ActiveJob::Base
5
+ queue_as :analytics
6
+
7
+ def perform data
8
+ GA::Client.new.call data
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,15 @@
1
+ module BG
2
+ module Common
3
+ module Analytics
4
+ class IntercomCreateUserJob < ::ActiveJob::Base
5
+ queue_as :analytics
6
+
7
+ def perform data
8
+ Intercom::Client.new.make_intercom_call do |client|
9
+ client.users.create data
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,13 @@
1
+ module BG
2
+ module Common
3
+ module Analytics
4
+ class IntercomEventJob < ::ActiveJob::Base
5
+ queue_as :analytics
6
+
7
+ def perform data
8
+ Intercom::Client.new.call data
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,15 @@
1
+ module BG
2
+ module Common
3
+ module Analytics
4
+ class IntercomExportUsersJob < ::ActiveJob::Base
5
+ queue_as :analytics
6
+
7
+ def perform data
8
+ Intercom::Client.new.make_intercom_call do |client|
9
+ client.users.submit_bulk_job(create_items: data)
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,17 @@
1
+ module BG
2
+ module Common
3
+ module Analytics
4
+ class IntercomSyncUsersJob < ::ActiveJob::Base
5
+ queue_as :analytics
6
+
7
+ def perform
8
+ users = User.all
9
+
10
+ users.each do |user|
11
+ Analytics::Intercom.update_user user
12
+ end unless users.blank?
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,13 @@
1
+ module BG
2
+ module Common
3
+ module Analytics
4
+ class KeenEventJob < ::ActiveJob::Base
5
+ queue_as :analytics
6
+
7
+ def perform event, data
8
+ Keen::Client.new.call event, data
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
data/bg-common.gemspec ADDED
@@ -0,0 +1,26 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'bg/common/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "bg-common"
8
+ spec.version = BG::Common::VERSION
9
+ spec.authors = ["Youssef Chaker"]
10
+ spec.email = ["youssef@bearandgiraffe.com"]
11
+
12
+ spec.summary = 'A Ruby library of common behaviors, especially for Ruby on Rails applications.'
13
+ spec.description = 'A Ruby library of common behaviors, especially for Ruby on Rails applications.'
14
+ spec.homepage = 'https://github.com/bearandgiraffe/bg-common'
15
+
16
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
17
+ f.match(%r{^(test|spec|features)/})
18
+ end
19
+ spec.bindir = 'exe'
20
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
21
+ spec.require_paths = ['lib']
22
+
23
+ spec.add_development_dependency 'bundler', '~> 1.14'
24
+ spec.add_development_dependency 'rake', '~> 10.0'
25
+ spec.add_development_dependency 'minitest', '~> 5.0'
26
+ end
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "bg/common"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,12 @@
1
+ require_relative 'google_analytics/client'
2
+ require_relative 'google_analytics/events'
3
+
4
+ module BG
5
+ module Common
6
+ module Analytics
7
+ module GA
8
+ extend Events
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,27 @@
1
+ module BG
2
+ module Common
3
+ module Analytics
4
+ module GA
5
+ class Client
6
+ def initialize
7
+ @ga = ::Gabba::Gabba.new(ENV['GA_TRACKER_CODE'], ENV['GA_DOMAIN'])
8
+ end
9
+
10
+ def ga
11
+ @ga ||= ::Gabba::Gabba.new(ENV['GA_TRACKER_CODE'], ENV['GA_DOMAIN'])
12
+ end
13
+
14
+ def call data
15
+ ga.event(
16
+ data[:category],
17
+ data[:action],
18
+ data[:label],
19
+ data[:value],
20
+ data[:bounce] || false
21
+ )
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,40 @@
1
+ #
2
+ # Module that wraps the GA events API to create specific events related
3
+ # to actions from the app.
4
+ #
5
+ module BG
6
+ module Common
7
+ module Analytics
8
+ module GA
9
+ module Events
10
+ #
11
+ # Creates an event in GA
12
+ #
13
+ # @param data, Hash
14
+ #
15
+ # The data param includes the information for the event to register on GA.
16
+ # It should have the following attributes:
17
+ #
18
+ # category: String
19
+ # action: String
20
+ # label: String
21
+ # value: Integer
22
+ # bounce: Boolean
23
+ #
24
+ # more info here:
25
+ # https://support.google.com/analytics/answer/1033068#Anatomy
26
+ # https://developers.google.com/analytics/devguides/collection/analyticsjs/events
27
+ #
28
+ def create_event data
29
+ if BG::Common.active_job?
30
+ GAEventJob.perform_later data
31
+ else
32
+ Client.new.call data
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
40
+
@@ -0,0 +1,39 @@
1
+ module BG
2
+ module Common
3
+ module Analytics
4
+ module Intercom
5
+ class Client
6
+ def initialize
7
+ @intercom = ::Intercom::Client.new(app_id: config.app_id, api_key: config.api_key)
8
+ end
9
+
10
+ def intercom
11
+ @intercom ||= ::Intercom::Client.new(app_id: config.app_id, api_key: config.api_key)
12
+ end
13
+
14
+ def call data
15
+ make_intercom_call do |client|
16
+ client.events.create data
17
+ end
18
+ end
19
+
20
+ def make_intercom_call &block
21
+ begin
22
+ yield self.intercom
23
+ rescue ::Intercom::IntercomError => e
24
+ # TODO: report this to newrelic so we won't lose any important
25
+ # errors that we might have to fix.
26
+ BG::Common.logger.debug "Intercom call failed: #{e.inspect}"
27
+ end
28
+ end
29
+
30
+ private
31
+
32
+ def config
33
+ ::IntercomRails.config
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,36 @@
1
+ #
2
+ # Module that wraps the Intercom events API to create specific
3
+ # events related to app actions.
4
+ #
5
+ module BG
6
+ module Common
7
+ module Analytics
8
+ module Intercom
9
+ module Events
10
+ #
11
+ # Creates an event in Intercom
12
+ #
13
+ # @param data, Hash
14
+ #
15
+ # The data param includes the information for the event to register on
16
+ # Intercom. It should have the following attributes:
17
+ #
18
+ # event_name: String
19
+ # created_at: Timestamp
20
+ # email: String
21
+ # metadata: Object
22
+ #
23
+ # more info here: https://doc.intercom.io/api/#submitting-events
24
+ #
25
+ def create_event data
26
+ if BG::Common.active_job?
27
+ IntercomEventJob.perform_later data
28
+ else
29
+ Client.new.call data
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,67 @@
1
+ #
2
+ # Module that wraps the Intercom users API.
3
+ #
4
+ module BG
5
+ module Common
6
+ module Analytics
7
+ module Intercom
8
+ module User
9
+ #
10
+ # Exports users to Intercom.
11
+ #
12
+ # @param limit, Integer, Sets the limit of users to export.
13
+ #
14
+ def export_users limit=nil
15
+ users = ::User.all
16
+
17
+ if limit
18
+ users = users.limit(limit)
19
+ end
20
+
21
+ data = users.map { |user| intercom_user_object user }
22
+
23
+ IntercomExportUsersJob.perform_later data
24
+ end
25
+
26
+ #
27
+ # Creates user in Intercom.
28
+ #
29
+ # @param user, User
30
+ #
31
+ def create_user user
32
+ data = intercom_user_object user
33
+
34
+ IntercomCreateUserJob.perform_later data
35
+ end
36
+
37
+ #
38
+ # Updates user in Intercom.
39
+ #
40
+ # @param user, User
41
+ #
42
+ def update_user user
43
+ create_user user
44
+ end
45
+
46
+ private
47
+
48
+ def intercom_user_object user
49
+ {
50
+ user_id: user.id,
51
+ email: user.username,
52
+ name: user.name,
53
+ signed_up_at: user.created_at.to_i,
54
+ custom_attributes: custom_attributes(user)
55
+ }
56
+ end
57
+
58
+ def custom_attributes user
59
+ # Override this method in your app to provide custom attributes
60
+ # specific to your app to be passed on to Intercom.
61
+ { }
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,14 @@
1
+ require_relative 'intercom/client'
2
+ require_relative 'intercom/user'
3
+ require_relative 'intercom/events'
4
+
5
+ module BG
6
+ module Common
7
+ module Analytics
8
+ module Intercom
9
+ extend User
10
+ extend Events
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,16 @@
1
+ module BG
2
+ module Common
3
+ module Analytics
4
+ module Keen
5
+ class Client
6
+ def initialize
7
+ end
8
+
9
+ def call event, data = {}
10
+ ::Keen.publish event.to_sym, data
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end