userlist-rails 0.3.0 → 0.5.3

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: f4ac0764bfdecbc381ea029d3fa9e8e2b04aa7f668eff35f7f54d14b7e20d1e9
4
- data.tar.gz: 31e4be6a0b8fe3b5a7cf172b9a07af9e6e5dac7a9edd38751b39416ddee8ad9e
3
+ metadata.gz: 5c07d01a265094da4cbd48cf116bb6f883e22a2d3c4f6f70f36866ee02081f28
4
+ data.tar.gz: 2f6c5bf68403019e08188ce1cd7aeea7bda6233634633d7025161ffd54dee6f2
5
5
  SHA512:
6
- metadata.gz: 3053ce85e9cc0d218dec274d0a11b5e0fb9caac714b999a2e3a0995946b17ae5f0180159fd978ecc91c890230cf8c744d3e9ddcfe67f1b035eb19c610ce49bbd
7
- data.tar.gz: 0a426f64b71938dfea7112c62d96b97e568a47db3a8f1780571ad3612d0f7f39e8cc8c6f2511a5344b95785ad4efe18e8a5f9e912c886a9f2587601716bf2b46
6
+ metadata.gz: a009f824dbc922c3dca025e0fccc6e162d19fa23f87b15d6bfd65747a155e1307666a62c04b4266778f631c6a79c0167dfc19ce490ecc91f36c5d4c8d811de94
7
+ data.tar.gz: 950db541f0a4715430319f116eebd90a1fdca6dbf4cadb2d15d75e384b6fe24796dc3cd43c55eb26dae2fe3f680e968b8eaedf9ee70cb55f5d44267070dd6fc0
@@ -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
data/.rubocop.yml CHANGED
@@ -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:
data/CODE_OF_CONDUCT.md CHANGED
@@ -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 [![Build Status](https://travis-ci.com/userlistio/userlist-rails.svg?branch=master)](https://travis-ci.com/userlistio/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,9 +62,10 @@ 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
@@ -79,40 +92,191 @@ end
79
92
 
80
93
  To manually send user data into Userlist, use the `Userlist::Push.users.push` method.
81
94
 
95
+ ```ruby
96
+ Userlist::Push.users.push(user)
97
+ ```
98
+
99
+ It's also possible to customize the payload sent to Userlist by passing a hash instead of the user object.
100
+
82
101
  ```ruby
83
102
  Userlist::Push.users.push(identifier: user.id, email: user.email, properties: { first_name: user.first_name, last_name: user.last_name })
84
103
  ```
85
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
+
86
119
  It's also possible to delete a user from Userlist, using the `Userlist::Push.users.delete` method.
87
120
 
88
121
  ```ruby
89
- Userlist::Push.users.delete(user.id)
122
+ Userlist::Push.users.delete(user)
90
123
  ```
91
124
 
92
- ### Tracking Events
93
125
 
94
- To track custom events use the `Userlist::Push.events.push` method.
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.
167
+
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.
179
+
180
+ ```ruby
181
+ Userlist::Push.companies.delete(company)
182
+ ```
183
+
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.
95
187
 
96
188
  ```ruby
97
- Userlist::Push.events.push(name: 'project_created', user: current_user, properties: { project_name: project.name })
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
+ }
98
208
  ```
99
209
 
100
- It is possible to make the `user` property optional by setting it for the entire request using an `around_action` callback in your `ApplicationController`.
210
+ Similar to users and events, these relationships may define a `userlist_properties` method to provide addition properties that describe the relationship.
101
211
 
102
212
  ```ruby
103
- class ApplicationController < ActionController::Base
104
- around_action :setup_userlist_current_user
213
+ class Membership < ApplicationRecord
214
+ belongs_to :user
215
+ belongs_to :account
105
216
 
106
- def setup_userlist_current_user(&block)
107
- Userlist::Rails.with_current_user(current_user, &block)
217
+ def userlist_properties
218
+ { role: role }
108
219
  end
109
220
  end
110
221
  ```
111
222
 
112
- 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.
113
227
 
114
228
  ```ruby
115
- Userlist::Push.events.push(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) %>
116
280
  ```
117
281
 
118
282
  ### Batch importing
@@ -131,7 +295,7 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
131
295
 
132
296
  ## Contributing
133
297
 
134
- 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.
135
299
 
136
300
  ## License
137
301
 
@@ -139,4 +303,12 @@ The gem is available as open source under the terms of the [MIT License](https:/
139
303
 
140
304
  ## Code of Conduct
141
305
 
142
- 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/).
@@ -1,11 +1,16 @@
1
1
  require 'userlist/rails/config'
2
2
  require 'userlist/rails/railtie'
3
3
 
4
- require 'userlist/rails/extensions/resource'
5
- require 'userlist/rails/extensions/event'
6
-
7
4
  module Userlist
8
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
+
9
14
  def self.with_current_user(user)
10
15
  Thread.current[:userlist_current_user] = user
11
16
  yield
@@ -17,10 +22,23 @@ module Userlist
17
22
  Thread.current[:userlist_current_user]
18
23
  end
19
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
+
20
36
  def self.detect_model(*names)
21
37
  names.each do |name|
22
38
  begin
23
- return name.constantize
39
+ model = name.constantize
40
+
41
+ return model if model.is_a?(Class)
24
42
  rescue NameError
25
43
  false
26
44
  end
@@ -28,5 +46,81 @@ module Userlist
28
46
 
29
47
  nil
30
48
  end
49
+
50
+ def self.detect_relationship(from, to)
51
+ return unless reflection = find_reflection(from, to)
52
+
53
+ reflection.through_reflection.klass if reflection.through_reflection?
54
+ end
55
+
56
+ def self.find_reflection(from, to)
57
+ return unless from && to
58
+
59
+ from.reflect_on_all_associations.find { |r| r.class_name == to.to_s }
60
+ end
61
+
62
+ def self.setup_callbacks(model, scope)
63
+ return if model.instance_variable_get(:@userlist_callbacks_registered)
64
+
65
+ setup_callback(:create, model, scope, :push)
66
+ setup_callback(:update, model, scope, :push)
67
+ setup_callback(:destroy, model, scope, :delete)
68
+
69
+ model.instance_variable_set(:@userlist_callbacks_registered, true)
70
+ end
71
+
72
+ def self.setup_callback(type, model, scope, method)
73
+ return unless callback_method = [:after_commit, :"after_#{type}"].find { |m| model.respond_to?(m) }
74
+
75
+ callback = lambda do
76
+ begin
77
+ relation = Userlist::Push.public_send(scope)
78
+ relation.public_send(method, self)
79
+ rescue Userlist::Error => e
80
+ Userlist.logger.error "Failed to #{method} #{method.to_s.singularize}: #{e.message}"
81
+ end
82
+ end
83
+
84
+ model.public_send(callback_method, callback, on: type)
85
+ end
86
+
87
+ def self.setup_extensions
88
+ Userlist::Push::User.include(Userlist::Rails::Extensions::User)
89
+ Userlist::Push::Company.include(Userlist::Rails::Extensions::Company)
90
+ Userlist::Push::Relationship.include(Userlist::Rails::Extensions::Relationship)
91
+ Userlist::Push::Event.include(Userlist::Rails::Extensions::Event)
92
+ end
93
+
94
+ def self.check_deprecations(type)
95
+ deprecated_methods = (type.instance_methods + type.private_instance_methods) & DEPRECATED_MODEL_METHODS
96
+
97
+ if deprecated_methods.any?
98
+ raise <<~MESSAGE
99
+ Deprecation warning for userlist-rails
100
+
101
+ Customizing the way userlist-rails works has changed.
102
+
103
+ Using the #{deprecated_methods.to_sentence} method(s) on your #{type.name} model is not supported anymore.
104
+
105
+ 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
106
+ MESSAGE
107
+ end
108
+
109
+ deprecated_methods = type.private_instance_methods.grep(/userlist_/)
110
+
111
+ if deprecated_methods.any?
112
+ raise <<~MESSAGE
113
+ Deprecation warning for userlist-rails
114
+
115
+ Customizing the way userlist-rails works has changed.
116
+
117
+ Using private methods (like #{deprecated_methods.to_sentence}) on your #{type.name} model is not supported anymore. Please use public methods instead.
118
+
119
+ 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
120
+ MESSAGE
121
+ end
122
+
123
+ true
124
+ end
31
125
  end
32
126
  end