userlist-rails 0.2.1 → 0.5.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a8b5550e94f954f69d1b8447349a277afb45612c329088a521d7e1f719248d59
4
- data.tar.gz: c24fd494b7cf44973a7c1bcb8d472ff8ee2a4672df0d46e1738cdc149f789666
3
+ metadata.gz: c4361f8f73c39543b0250633d22c4d758ac4b43cb073d160bce946ac14390c39
4
+ data.tar.gz: 59e21c2a6579d36c4b50545f858dbb30c1a63e9e1dc7de69dacfbf87201707cf
5
5
  SHA512:
6
- metadata.gz: 9763a4bdd5fc8c21eef2fd55982f1ebecaafc3ef2a71af497f9e7000f9473aa4ed885a7174ee29919891e57603285aa100704d886489883a2a9cd7f1343c661d
7
- data.tar.gz: 81c787f5d451da159443c7d596beab58bb80a264ff05fdd3e4c140a4e8f2e5a114785bde78b32e7397a67d767aab68bcaa2bf6033273853f38e33899b5352813
6
+ metadata.gz: 776f506a31ac8db648d2e8fb6ccdf846abba9fe4d8c04f4b6b0143ebd5a6711719b6c627a7191016d529ed76c3c8ab880da797823d85b6846e767a89ff473e68
7
+ data.tar.gz: 1984ab2272f68b9b6dddea36cac787ec1bd278da08630b4c168fd25a3aa6fca1fead8936415f28d80c58532cc866cc9b1ea13512ad4cb4c5a202bcf84c598f11
@@ -0,0 +1,22 @@
1
+ name: Tests
2
+ on: [push]
3
+ jobs:
4
+ build:
5
+ strategy:
6
+ matrix:
7
+ ruby:
8
+ - 2.4
9
+ - 2.4
10
+ - 2.5
11
+ - 2.6
12
+ - 2.7
13
+ - 3.0
14
+ runs-on: ubuntu-latest
15
+ steps:
16
+ - uses: actions/checkout@v2
17
+ - uses: ruby/setup-ruby@v1
18
+ with:
19
+ ruby-version: ${{ matrix.ruby }}
20
+ bundler-cache: true
21
+ - name: RSpec
22
+ run: bundle exec rake
@@ -1,10 +1,12 @@
1
1
  AllCops:
2
- TargetRubyVersion: 2.2
2
+ TargetRubyVersion: 2.4
3
+ NewCops: enable
4
+ SuggestExtensions: false
3
5
  Exclude:
4
6
  - 'bin/*'
5
7
  - 'Guardfile'
6
8
 
7
- Metrics/LineLength:
9
+ Layout/LineLength:
8
10
  Enabled: false
9
11
 
10
12
  Metrics/ModuleLength:
@@ -32,13 +34,10 @@ Layout/ElseAlignment:
32
34
  Lint/AssignmentInCondition:
33
35
  Enabled: false
34
36
 
35
- Lint/EndAlignment:
36
- EnforcedStyleAlignWith: start_of_line
37
-
38
37
  Layout/AccessModifierIndentation:
39
38
  EnforcedStyle: outdent
40
39
 
41
- Layout/AlignParameters:
40
+ Layout/ParameterAlignment:
42
41
  EnforcedStyle: with_fixed_indentation
43
42
 
44
43
  Style/FrozenStringLiteralComment:
@@ -55,7 +55,7 @@ further defined and clarified by project maintainers.
55
55
  ## Enforcement
56
56
 
57
57
  Instances of abusive, harassing, or otherwise unacceptable behavior may be
58
- reported by contacting the project team at benedikt@benediktdeicke.com. All
58
+ reported by contacting the project team at support@userlist.com. All
59
59
  complaints will be reviewed and investigated and will result in a response that
60
60
  is deemed necessary and appropriate to the circumstances. The project team is
61
61
  obligated to maintain confidentiality with regard to the reporter of an incident.
data/Gemfile CHANGED
@@ -4,7 +4,7 @@ git_source(:github) { |repo_name| "https://github.com/#{repo_name}" }
4
4
 
5
5
  gemspec
6
6
 
7
- gem 'userlist', github: 'userlistio/userlist-ruby', branch: 'master'
7
+ gem 'userlist', github: 'userlist/userlist-ruby', branch: 'master'
8
8
 
9
9
  gem 'guard-rspec', '~> 4.7'
10
10
  gem 'guard-rubocop', '~> 1.3'
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
- # Userlist::Rails
1
+ # Userlist for Ruby on Rails [![Build Status](https://github.com/userlist/userlist-rails/workflows/Tests/badge.svg)](https://github.com/userlist/userlist-rails)
2
2
 
3
- This gem helps with integrating [Userlist.io](http://userlist.io) into Ruby on Rails applications.
3
+ This gem helps with integrating [Userlist](http://userlist.com) into Ruby on Rails applications.
4
4
 
5
5
  ## Installation
6
6
 
@@ -20,14 +20,15 @@ Or install it yourself as:
20
20
 
21
21
  ## Configuration
22
22
 
23
- The only required configuration is the Push API key. You can get your Push API key via the [Push API settings](https://app.userlist.io/settings/push) in your Userlist.io account.
23
+ The only required configuration is the Push API key. You can get your Push API key via the [Push API settings](https://app.userlist.com/settings/push) in your Userlist account.
24
24
 
25
- Configuration values can either be set via an initializer or as environment variables. The environment take precedence over configuration values from the initializer. Please refer to the [userlist-ruby](http://github.com/userlistio/userlist-ruby) gem for additional configuration options.
25
+ Configuration values can either be set via an initializer or as environment variables. The environment take precedence over configuration values from the initializer. Please refer to the [userlist](http://github.com/userlist/userlist-ruby) gem for additional configuration options.
26
26
 
27
27
  Configuration via environment variables:
28
28
 
29
29
  ```shell
30
- USERLIST_PUSH_KEY=401e5c498be718c0a38b7da7f1ce5b409c56132a49246c435ee296e07bf2be39
30
+ USERLIST_PUSH_KEY=VvB7pjDrv0V2hoaOCeZ5rIiUEPbEhSUN
31
+ USERLIST_PUSH_ID=6vPkJl44cm82y4aLBIzaOhuEHJd0Bm7b
31
32
  ```
32
33
 
33
34
  Configuration via an initializer:
@@ -35,13 +36,24 @@ Configuration via an initializer:
35
36
  ```ruby
36
37
  # config/initializer/userlist.rb
37
38
  Userlist.configure do |config|
38
- config.push_key = '401e5c498be718c0a38b7da7f1ce5b409c56132a49246c435ee296e07bf2be39'
39
+ config.push_key = 'VvB7pjDrv0V2hoaOCeZ5rIiUEPbEhSUN'
40
+ config.push_id = '6vPkJl44cm82y4aLBIzaOhuEHJd0Bm7b'
39
41
  end
40
42
  ```
41
43
 
44
+ In addition to the configuration options of the [userlist](http://github.com/userlist/userlist-ruby#configuration) gem, the following options are available.
45
+
46
+ | Name | Default value | Description |
47
+ | --------------- | ---------------------------- | -------------------------------------------------------------------------------------------------------------------- |
48
+ | `user_model` | `nil` | The user model to use. Will be automatically set when `auto_discover` is `true` |
49
+ | `company_model` | `nil` | The company model to use. Will be automatically set when `auto_discover` is `true` |
50
+ | `relationship_model` | `nil` | The relationship model to use. Will be automatically infered from the user and company models |
51
+ | `auto_discover` | `true` | The gem will try to automatically identify your `User` and `Company` models. Possible values are `true` and `false`. |
52
+ | `script_url` | `https://js.userlist.com/v1` | The script url to load the Userlist in-app messages script from. |
53
+
42
54
  ### Disabling in development and test environments
43
55
 
44
- As sending test and development data into data into Userlist isn't very desireable, you can disable transmissions by setting the push strategy to `:null`.
56
+ As sending test and development data into data into Userlist isn't very desirable, you can disable transmissions by setting the push strategy to `:null`.
45
57
 
46
58
  ```ruby
47
59
  # config/initializer/userlist.rb
@@ -50,14 +62,15 @@ Userlist.configure do |config|
50
62
  end
51
63
  ```
52
64
 
53
-
54
65
  ## Usage
55
66
 
67
+ > ⚠️ **Important:** If you're using [Segment](https://segment.com) in combination with this gem, please make sure that both are using the same user identifier. By default, this gem will send `"user-#{id}"` (a combination of the user's primary key in the database and the `user-` prefix) as identifier. Either customize the `userlist_identifier` method on your User model, or ensure that you use the same identifier in your Segment integration.
68
+
56
69
  ### Tracking Users
57
70
 
58
71
  #### Sending user data automatically
59
72
 
60
- By default, this gem will automatically detect your `User` model and create and update the corresponding user inside of Userlist. To customize the `identifier`, `email`, or `properties` transmitted for a user, you can overwrite the according methods in your `User` model.
73
+ By default, this gem will automatically detect your `User` model and create, update, and delete the corresponding user inside of Userlist. To customize the `identifier`, `email`, or `properties` transmitted for a user, you can overwrite the according methods in your `User` model.
61
74
 
62
75
  ```ruby
63
76
  class User < ApplicationRecord
@@ -77,36 +90,193 @@ end
77
90
 
78
91
  #### Sending user data manually
79
92
 
80
- To manually send user data into Userlist, use the `Userlist::Push.user` method.
93
+ To manually send user data into Userlist, use the `Userlist::Push.users.push` method.
81
94
 
82
95
  ```ruby
83
- Userlist::Push.user(identifier: user.id, email: user.email, properties: { first_name: user.first_name, last_name: user.last_name })
96
+ Userlist::Push.users.push(user)
84
97
  ```
85
98
 
86
- ### Tracking Events
99
+ It's also possible to customize the payload sent to Userlist by passing a hash instead of the user object.
100
+
101
+ ```ruby
102
+ Userlist::Push.users.push(identifier: user.id, email: user.email, properties: { first_name: user.first_name, last_name: user.last_name })
103
+ ```
104
+
105
+ #### Ignoring users
106
+
107
+ For cases where you don't want to send specific user to Userlist you can add a `userlist_push?` method. Whenever this method doesn't return a falsey value, this user will not be sent to Userlist. This also applies to any events or relationships this user is involved in.
108
+
109
+ ```ruby
110
+ class User < ApplicationRecord
111
+ def userlist_push?
112
+ !deleted? && !guest?
113
+ end
114
+ end
115
+ ```
116
+
117
+ #### Deleting users
118
+
119
+ It's also possible to delete a user from Userlist, using the `Userlist::Push.users.delete` method.
120
+
121
+ ```ruby
122
+ Userlist::Push.users.delete(user)
123
+ ```
124
+
125
+
126
+ ### Tracking Companies
127
+
128
+ #### Sending company data automatically
129
+
130
+ By default, this gem will automatically detect your company model (like `Account`, `Company`, `Team`, `Organization`) and create, update, and delete the corresponding company inside of Userlist. To customize the `identifier`, `name`, or `properties` transmitted for a company, you can overwrite the according methods in your company model.
131
+
132
+ ```ruby
133
+ class Account < ApplicationRecord
134
+ def userlist_properties
135
+ { projects: projects.count }
136
+ end
137
+
138
+ def userlist_identifier
139
+ "account-#{id}"
140
+ end
141
+
142
+ def userlist_name
143
+ name
144
+ end
145
+ end
146
+ ```
147
+
148
+
149
+ #### Sending company data manually
150
+
151
+ To manually send company data into Userlist, use the `Userlist::Push.companies.push` method.
152
+
153
+ ```ruby
154
+ Userlist::Push.companies.push(user)
155
+ ```
156
+
157
+ It's also possible to customize the payload sent to Userlist by passing a hash instead of the company object.
158
+
159
+ ```ruby
160
+ Userlist::Push.companies.push(identifier: company.id, name: company.name, properties: { projects: company.projects.count })
161
+ ```
162
+
163
+
164
+ #### Ignoring companies
165
+
166
+ For cases where you don't want to send specific company to Userlist you can add a `userlist_push?` method. Whenever this method doesn't return a falsey value, this company will not be sent to Userlist. This also applies to any events or relationships this company is involved in.
87
167
 
88
- To track custom events use the `Userlist::Push.event` method.
168
+ ```ruby
169
+ class User < ApplicationRecord
170
+ def userlist_push?
171
+ !deleted? && !guest?
172
+ end
173
+ end
174
+ ```
175
+
176
+ #### Deleting users
177
+
178
+ It's also possible to delete a company from Userlist, using the `Userlist::Push.companies.delete` method.
89
179
 
90
180
  ```ruby
91
- Userlist::Push.event(name: 'project_created', user: current_user, properties: { project_name: project.name })
181
+ Userlist::Push.companies.delete(company)
92
182
  ```
93
183
 
94
- It is possible to make the `user` property optional by setting it for the entire request using an `around_action` callback in your `ApplicationController`.
184
+ ### Tracking relationships
185
+
186
+ Userlist supports n:m relationships between users and companies. This gem will try to figure out the model your application uses to describe these relationships by looking at the associations defined in your user and company models. When sending a user to Userlist, this gem will try to automatically include the user's relationships as well. This includes information about the relationships and companies this user is associated with, but not information about other users associated with any of the companies. This works the other way around as well. When sending a company, it'll try to automatically include the company's relationships, but not any information about the associated users' other companies.
187
+
188
+ ```ruby
189
+ user = User.create(email: 'foo@example.com')
190
+ user.companies.create(name: 'Example, Inc.')
191
+
192
+ Userlist::Push.users.push(user)
193
+
194
+ # Payload sent to Userlist
195
+ {
196
+ identifier: 'user-1',
197
+ email: 'foo@example.com',
198
+ relationships: [
199
+ {
200
+ user: 'user-identifier',
201
+ company: {
202
+ identifier: 'company-identifier',
203
+ name: 'Example, Inc.',
204
+ }
205
+ }
206
+ ]
207
+ }
208
+ ```
209
+
210
+ Similar to users and events, these relationships may define a `userlist_properties` method to provide addition properties that describe the relationship.
95
211
 
96
212
  ```ruby
97
- class ApplicationController < ActionController::Base
98
- around_action :setup_userlist_current_user
213
+ class Membership < ApplicationRecord
214
+ belongs_to :user
215
+ belongs_to :account
99
216
 
100
- def setup_userlist_current_user(&block)
101
- Userlist::Rails.with_current_user(current_user, &block)
217
+ def userlist_properties
218
+ { role: role }
102
219
  end
103
220
  end
104
221
  ```
105
222
 
106
- This simplifies the tracking call for the current request.
223
+
224
+ #### Customizing relationship lookup
225
+
226
+ It's possible to customize the way this gem looks up relationships for users and companies by specifying a `userlist_relationships` method on the user and/or company model.
107
227
 
108
228
  ```ruby
109
- Userlist::Push.event(name: 'project_created', properties: { project_name: project.name })
229
+ class User < ApplicationRecord
230
+ def userlist_relationships
231
+ memberships.where(role: 'owner')
232
+ end
233
+ end
234
+ ```
235
+
236
+
237
+ #### Ignoring relationships
238
+
239
+ This gem automatically ignore relationship if either the user or the company is ignored. However, in some cases it might be desirable to ignore relationships even when they connect to valid objects. A typical example for this are pending invitations. To support this use case, you can provide a `userlist_push?` method. Whenever this method doesn't return a falsey value, this relationship will not be sent to Userlist.
240
+
241
+ ```ruby
242
+ class Membership < ApplicationRecord
243
+ belongs_to :user
244
+ belongs_to :account
245
+
246
+ def userlist_push?
247
+ pending?
248
+ end
249
+ end
250
+ ```
251
+
252
+ #### Deleting relationships
253
+
254
+ It's also possible to delete a relationship from Userlist, using the `Userlist::Push.relationship.delete` method.
255
+
256
+ ```ruby
257
+ Userlist::Push.relationship.delete(membership)
258
+ ```
259
+
260
+ ### Tracking Events
261
+
262
+ To track custom events use the `Userlist::Push.events.push` method. Events can be related to a user, a company, or both.
263
+
264
+ ```ruby
265
+ Userlist::Push.events.push(name: 'project_created', user: current_user, company: current_account, properties: { project_name: project.name })
266
+ ```
267
+
268
+ ### Enabling in-app messages
269
+
270
+ In order to use in-app messages, please set both the `push_key` and `push_id` configuration variables. Afterwards, include the `userlist_script_tag` helper into your application's layout for signed in users.
271
+
272
+ ```erb
273
+ <%= userlist_script_tag %>
274
+ ```
275
+
276
+ By default, the helper will try to use the `current_user` helper to read the currently signed in user. You can change the default bebahvior by passing a different user. The passed object must respond to the `userlist_identifier` method.
277
+
278
+ ```erb
279
+ <%= userlist_script_tag(user) %>
110
280
  ```
111
281
 
112
282
  ### Batch importing
@@ -125,7 +295,7 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
125
295
 
126
296
  ## Contributing
127
297
 
128
- Bug reports and pull requests are welcome on GitHub at https://github.com/userlistio/userlist-rails. 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.
298
+ Bug reports and pull requests are welcome on GitHub at <https://github.com/userlist/userlist-rails>. 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.
129
299
 
130
300
  ## License
131
301
 
@@ -133,4 +303,12 @@ The gem is available as open source under the terms of the [MIT License](https:/
133
303
 
134
304
  ## Code of Conduct
135
305
 
136
- Everyone interacting in the Userlist::Rails project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/userlistio/userlist-rails/blob/master/CODE_OF_CONDUCT.md).
306
+ Everyone interacting in the Userlist::Rails project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/userlist/userlist-rails/blob/master/CODE_OF_CONDUCT.md).
307
+
308
+ ## What is Userlist?
309
+
310
+ [![Userlist](https://userlist.com/images/external/userlist-logo-github.svg)](https://userlist.com/)
311
+
312
+ [Userlist](https://userlist.com/) allows you to onboard and engage your SaaS users with targeted behavior-based campaigns using email or in-app messages.
313
+
314
+ Userlist was started in 2017 as an alternative to bulky enterprise messaging tools. We believe that running SaaS products should be more enjoyable. Learn more [about us](https://userlist.com/about-us/).
@@ -3,6 +3,14 @@ require 'userlist/rails/railtie'
3
3
 
4
4
  module Userlist
5
5
  module Rails
6
+ DEPRECATED_MODEL_METHODS = [
7
+ :userlist_push,
8
+ :userlist_delete,
9
+ :userlist_payload,
10
+ :userlist_company,
11
+ :userlist_user
12
+ ].freeze
13
+
6
14
  def self.with_current_user(user)
7
15
  Thread.current[:userlist_current_user] = user
8
16
  yield
@@ -14,6 +22,17 @@ module Userlist
14
22
  Thread.current[:userlist_current_user]
15
23
  end
16
24
 
25
+ def self.with_current_company(company)
26
+ Thread.current[:userlist_current_company] = company
27
+ yield
28
+ ensure
29
+ Thread.current[:userlist_current_company] = nil
30
+ end
31
+
32
+ def self.current_company
33
+ Thread.current[:userlist_current_company]
34
+ end
35
+
17
36
  def self.detect_model(*names)
18
37
  names.each do |name|
19
38
  begin
@@ -25,5 +44,81 @@ module Userlist
25
44
 
26
45
  nil
27
46
  end
47
+
48
+ def self.detect_relationship(from, to)
49
+ return unless reflection = find_reflection(from, to)
50
+
51
+ reflection.through_reflection.klass if reflection.through_reflection?
52
+ end
53
+
54
+ def self.find_reflection(from, to)
55
+ return unless from && to
56
+
57
+ from.reflect_on_all_associations.find { |r| r.class_name == to.to_s }
58
+ end
59
+
60
+ def self.setup_callbacks(model, scope)
61
+ return if model.instance_variable_get(:@userlist_callbacks_registered)
62
+
63
+ setup_callback(:create, model, scope, :push)
64
+ setup_callback(:update, model, scope, :push)
65
+ setup_callback(:destroy, model, scope, :delete)
66
+
67
+ model.instance_variable_set(:@userlist_callbacks_registered, true)
68
+ end
69
+
70
+ def self.setup_callback(type, model, scope, method)
71
+ return unless callback_method = [:after_commit, :"after_#{type}"].find { |m| model.respond_to?(m) }
72
+
73
+ callback = lambda do
74
+ begin
75
+ relation = Userlist::Push.public_send(scope)
76
+ relation.public_send(method, self)
77
+ rescue Userlist::Error => e
78
+ Userlist.logger.error "Failed to #{method} #{method.to_s.singularize}: #{e.message}"
79
+ end
80
+ end
81
+
82
+ model.public_send(callback_method, callback, on: type)
83
+ end
84
+
85
+ def self.setup_extensions
86
+ Userlist::Push::User.include(Userlist::Rails::Extensions::User)
87
+ Userlist::Push::Company.include(Userlist::Rails::Extensions::Company)
88
+ Userlist::Push::Relationship.include(Userlist::Rails::Extensions::Relationship)
89
+ Userlist::Push::Event.include(Userlist::Rails::Extensions::Event)
90
+ end
91
+
92
+ def self.check_deprecations(type)
93
+ deprecated_methods = (type.instance_methods + type.private_instance_methods) & DEPRECATED_MODEL_METHODS
94
+
95
+ if deprecated_methods.any?
96
+ raise <<~MESSAGE
97
+ Deprecation warning for userlist-rails
98
+
99
+ Customizing the way userlist-rails works has changed.
100
+
101
+ Using the #{deprecated_methods.to_sentence} method(s) on your #{type.name} model is not supported anymore.
102
+
103
+ For details on how to customize the gem's behavior, please see https://github.com/userlist/userlist-rails or reach out to support@userlist.com
104
+ MESSAGE
105
+ end
106
+
107
+ deprecated_methods = type.private_instance_methods.grep(/userlist_/)
108
+
109
+ if deprecated_methods.any?
110
+ raise <<~MESSAGE
111
+ Deprecation warning for userlist-rails
112
+
113
+ Customizing the way userlist-rails works has changed.
114
+
115
+ Using private methods (like #{deprecated_methods.to_sentence}) on your #{type.name} model is not supported anymore. Please use public methods instead.
116
+
117
+ For details on how to customize the gem's behavior, please see https://github.com/userlist/userlist-rails or reach out to support@userlist.com
118
+ MESSAGE
119
+ end
120
+
121
+ true
122
+ end
28
123
  end
29
124
  end