userlist-rails 0.5.0 → 0.6.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: 9fe4b336d98e1d3015aa44aa9a261c37bf160747573d6507f44f533a14e6e4dd
4
- data.tar.gz: 77261e5a6972ce2b0f0d0ea43e141c3d4c33011790c01a8fd32749743c35b342
3
+ metadata.gz: ca257f164074da93de47984ddd695b818510ab5e671a1e4545eb986663d34335
4
+ data.tar.gz: 6fb32d37480a4af75a79f82c8ee76e180d0a29eed7b6a86fa09ea12e749544c2
5
5
  SHA512:
6
- metadata.gz: a3843cb90d6f4868179c43972e3b0877f8d1cd2e02637b03799f9778571d1115c44ee6c7dc320407d50e0cea7305d1b019d6bb63526b42f8dd109d180b015834
7
- data.tar.gz: 5147105865f3bfccc0750823f69889e76c34d7cd2f18b149fc6cd4ffc8bb7e80a3103cccd88dc14a2f3520bf9747b1cfbceaee1cf313ae37ecec3cf2a5fcc406
6
+ metadata.gz: '049815dabf0844173db83e9186e19e1736db9f0c14a9754f14203bcca1129b8be5411127e48212830b7951f6ff4d35cb9e36647c7af7214b607d47932400b96c'
7
+ data.tar.gz: ab1a3d8c38682a1de52ed54cae85c210a13b037e4fef8e83dc8c1cbc6381fcdad6727813e294e82411ae060169397963f282739722df38007a9e186d4127e4f7
data/Gemfile CHANGED
@@ -4,7 +4,5 @@ git_source(:github) { |repo_name| "https://github.com/#{repo_name}" }
4
4
 
5
5
  gemspec
6
6
 
7
- gem 'userlist', github: 'userlist/userlist-ruby', branch: 'master'
8
-
9
7
  gem 'guard-rspec', '~> 4.7'
10
8
  gem 'guard-rubocop', '~> 1.3'
data/README.md CHANGED
@@ -47,6 +47,7 @@ In addition to the configuration options of the [userlist](http://github.com/use
47
47
  | --------------- | ---------------------------- | -------------------------------------------------------------------------------------------------------------------- |
48
48
  | `user_model` | `nil` | The user model to use. Will be automatically set when `auto_discover` is `true` |
49
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 |
50
51
  | `auto_discover` | `true` | The gem will try to automatically identify your `User` and `Company` models. Possible values are `true` and `false`. |
51
52
  | `script_url` | `https://js.userlist.com/v1` | The script url to load the Userlist in-app messages script from. |
52
53
 
@@ -101,36 +102,181 @@ It's also possible to customize the payload sent to Userlist by passing a hash i
101
102
  Userlist::Push.users.push(identifier: user.id, email: user.email, properties: { first_name: user.first_name, last_name: user.last_name })
102
103
  ```
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
+
104
119
  It's also possible to delete a user from Userlist, using the `Userlist::Push.users.delete` method.
105
120
 
106
121
  ```ruby
107
122
  Userlist::Push.users.delete(user)
108
123
  ```
109
124
 
110
- ### Tracking Events
111
125
 
112
- 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.
113
152
 
114
153
  ```ruby
115
- Userlist::Push.events.push(name: 'project_created', user: current_user, properties: { project_name: project.name })
154
+ Userlist::Push.companies.push(company)
116
155
  ```
117
156
 
118
- It is possible to make the `user` property optional by setting it for the entire request using an `around_action` callback in your `ApplicationController`.
157
+ It's also possible to customize the payload sent to Userlist by passing a hash instead of the company object.
119
158
 
120
159
  ```ruby
121
- class ApplicationController < ActionController::Base
122
- around_action :setup_userlist_current_user
160
+ Userlist::Push.companies.push(identifier: company.id, name: company.name, properties: { projects: company.projects.count })
161
+ ```
123
162
 
124
- def setup_userlist_current_user(&block)
125
- Userlist::Rails.with_current_user(current_user, &block)
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 Account < ApplicationRecord
170
+ def userlist_push?
171
+ !deleted? && active_subscription?
126
172
  end
127
173
  end
128
174
  ```
129
175
 
130
- This simplifies the tracking call for the current request.
176
+ #### Deleting companies
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.
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.
211
+
212
+ ```ruby
213
+ class Membership < ApplicationRecord
214
+ belongs_to :user
215
+ belongs_to :account
216
+
217
+ def userlist_properties
218
+ { role: role }
219
+ end
220
+ end
221
+ ```
222
+
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.
227
+
228
+ ```ruby
229
+ class User < ApplicationRecord
230
+ def userlist_relationships
231
+ memberships.where(role: 'owner')
232
+ end
233
+ end
234
+ ```
235
+
236
+ If you don't have a dedicated model describing the relationship, you can return a hash including both the user and the company model.
237
+
238
+ ```ruby
239
+ class User < ApplicationRecord
240
+ def userlist_relationships
241
+ [
242
+ {
243
+ user: self,
244
+ company: account
245
+ }
246
+ ]
247
+ end
248
+ end
249
+ ```
250
+
251
+ #### Ignoring relationships
252
+
253
+ 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.
254
+
255
+ ```ruby
256
+ class Membership < ApplicationRecord
257
+ belongs_to :user
258
+ belongs_to :account
259
+
260
+ def userlist_push?
261
+ pending?
262
+ end
263
+ end
264
+ ```
265
+
266
+ #### Deleting relationships
267
+
268
+ It's also possible to delete a relationship from Userlist, using the `Userlist::Push.relationship.delete` method.
269
+
270
+ ```ruby
271
+ Userlist::Push.relationship.delete(membership)
272
+ ```
273
+
274
+ ### Tracking Events
275
+
276
+ To track custom events use the `Userlist::Push.events.push` method. Events can be related to a user, a company, or both.
131
277
 
132
278
  ```ruby
133
- Userlist::Push.events.push(name: 'project_created', properties: { project_name: project.name })
279
+ Userlist::Push.events.push(name: 'project_created', user: current_user, company: current_account, properties: { project_name: project.name })
134
280
  ```
135
281
 
136
282
  ### Enabling in-app messages
@@ -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
@@ -28,7 +36,9 @@ module Userlist
28
36
  def self.detect_model(*names)
29
37
  names.each do |name|
30
38
  begin
31
- return name.constantize
39
+ model = name.constantize
40
+
41
+ return model if model.is_a?(Class)
32
42
  rescue NameError
33
43
  false
34
44
  end
@@ -49,6 +59,12 @@ module Userlist
49
59
  from.reflect_on_all_associations.find { |r| r.class_name == to.to_s }
50
60
  end
51
61
 
62
+ def self.find_association_between(from, to)
63
+ return unless association = Userlist::Rails.find_reflection(from, to)
64
+
65
+ association.through_reflection || association
66
+ end
67
+
52
68
  def self.setup_callbacks(model, scope)
53
69
  return if model.instance_variable_get(:@userlist_callbacks_registered)
54
70
 
@@ -80,5 +96,37 @@ module Userlist
80
96
  Userlist::Push::Relationship.include(Userlist::Rails::Extensions::Relationship)
81
97
  Userlist::Push::Event.include(Userlist::Rails::Extensions::Event)
82
98
  end
99
+
100
+ def self.check_deprecations(type)
101
+ deprecated_methods = (type.instance_methods + type.private_instance_methods) & DEPRECATED_MODEL_METHODS
102
+
103
+ if deprecated_methods.any?
104
+ raise <<~MESSAGE
105
+ Deprecation warning for userlist-rails
106
+
107
+ Customizing the way userlist-rails works has changed.
108
+
109
+ Using the #{deprecated_methods.to_sentence} method(s) on your #{type.name} model is not supported anymore.
110
+
111
+ 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
112
+ MESSAGE
113
+ end
114
+
115
+ deprecated_methods = type.private_instance_methods.grep(/userlist_/)
116
+
117
+ if deprecated_methods.any?
118
+ raise <<~MESSAGE
119
+ Deprecation warning for userlist-rails
120
+
121
+ Customizing the way userlist-rails works has changed.
122
+
123
+ Using private methods (like #{deprecated_methods.to_sentence}) on your #{type.name} model is not supported anymore. Please use public methods instead.
124
+
125
+ 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
126
+ MESSAGE
127
+ end
128
+
129
+ true
130
+ end
83
131
  end
84
132
  end
@@ -34,6 +34,10 @@ module Userlist
34
34
  (model = super) && model.is_a?(Class) ? model : model&.to_s&.constantize
35
35
  end
36
36
 
37
+ def relationship_model
38
+ (model = super) && model.is_a?(Class) ? model : model&.to_s&.constantize
39
+ end
40
+
37
41
  Userlist::Config.send(:prepend, self)
38
42
  end
39
43
  end
@@ -53,17 +53,19 @@ module Userlist
53
53
  Userlist.logger.info('Automatically discovering models')
54
54
 
55
55
  userlist.user_model = Userlist::Rails.detect_model('User')
56
- userlist.company_model = Userlist::Rails.detect_model('Account', 'Company')
56
+ userlist.company_model = Userlist::Rails.detect_model('Account', 'Company', 'Team', 'Organization')
57
57
  userlist.relationship_model = Userlist::Rails.detect_relationship(userlist.user_model, userlist.company_model)
58
58
  end
59
59
 
60
60
  if user_model = userlist.user_model
61
61
  Userlist.logger.info("Preparing user model #{user_model}")
62
+ Userlist::Rails.check_deprecations(user_model)
62
63
  Userlist::Rails.setup_callbacks(user_model, :users)
63
64
  end
64
65
 
65
66
  if company_model = userlist.company_model
66
67
  Userlist.logger.info("Preparing company model #{company_model}")
68
+ Userlist::Rails.check_deprecations(company_model)
67
69
  Userlist::Rails.setup_callbacks(company_model, :companies)
68
70
  end
69
71
 
@@ -11,7 +11,7 @@ module Userlist
11
11
  end
12
12
 
13
13
  def [](name)
14
- public_send(name) if key?(name)
14
+ model.try("userlist_#{name}") || public_send("default_#{name}") if key?(name)
15
15
  end
16
16
 
17
17
  def key?(name)
@@ -27,11 +27,11 @@ module Userlist
27
27
  end
28
28
 
29
29
  def push?
30
- (!model.respond_to?(:push?) || model.push?)
30
+ (!model.respond_to?(:userlist_push?) || model.userlist_push?)
31
31
  end
32
32
 
33
33
  def delete?
34
- (!model.respond_to?(:delete?) || model.delete?)
34
+ (!model.respond_to?(:userlist_delete?) || model.userlist_delete?)
35
35
  end
36
36
 
37
37
  private
@@ -1,9 +1,12 @@
1
1
  require 'userlist/rails/transform'
2
+ require 'userlist/rails/transforms/has_relationships'
2
3
 
3
4
  module Userlist
4
5
  module Rails
5
6
  module Transforms
6
7
  class Company < Userlist::Rails::Transform
8
+ include HasRelationships
9
+
7
10
  def self.attributes
8
11
  @attributes ||= [
9
12
  :identifier,
@@ -14,26 +17,37 @@ module Userlist
14
17
  ]
15
18
  end
16
19
 
17
- def identifier
18
- model.try(:userlist_identifier) || "#{model.class.name}-#{model.id}".parameterize
20
+ def default_identifier
21
+ "#{model.class.name}-#{model.id}".parameterize
19
22
  end
20
23
 
21
- def properties
22
- model.try(:userlist_properties) || {}
24
+ def default_properties
25
+ {}
23
26
  end
24
27
 
25
- def relationships
26
- relationships_method = Userlist::Rails.find_reflection(config.company_model, config.relationship_model)&.name
28
+ def default_name
29
+ model.try(:name)
30
+ end
27
31
 
28
- model.try(:userlist_relationships) || (relationships_method && model.try(relationships_method))
32
+ def default_signed_up_at
33
+ model.try(:created_at)
29
34
  end
30
35
 
31
- def name
32
- model.try(:userlist_name) || model.try(:name)
36
+ private
37
+
38
+ def build_relationship(record)
39
+ {
40
+ user: record,
41
+ company: model
42
+ }
33
43
  end
34
44
 
35
- def signed_up_at
36
- model.try(:created_at)
45
+ def relationship_from
46
+ config.company_model
47
+ end
48
+
49
+ def relationship_to
50
+ config.user_model
37
51
  end
38
52
  end
39
53
  end
@@ -0,0 +1,35 @@
1
+ require 'userlist/rails/transform'
2
+
3
+ module Userlist
4
+ module Rails
5
+ module Transforms
6
+ module HasRelationships
7
+ def default_relationships
8
+ return unless association = Userlist::Rails.find_association_between(relationship_from, relationship_to)
9
+
10
+ records = model.try(association.name)
11
+
12
+ if association.klass == config.relationship_model
13
+ records
14
+ elsif association.klass == relationship_to
15
+ Array.wrap(records).map { |record| build_relationship(record) }
16
+ end
17
+ end
18
+
19
+ private
20
+
21
+ def build_relationship(_record)
22
+ raise NotImplementedError
23
+ end
24
+
25
+ def relationship_to
26
+ raise NotImplementedError
27
+ end
28
+
29
+ def relationship_from
30
+ raise NotImplementedError
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -12,20 +12,20 @@ module Userlist
12
12
  ]
13
13
  end
14
14
 
15
- def properties
16
- model.try(:userlist_properties) || {}
15
+ def default_properties
16
+ {}
17
17
  end
18
18
 
19
- def user
19
+ def default_user
20
20
  user_method = Userlist::Rails.find_reflection(config.relationship_model, config.user_model)&.name
21
21
 
22
- model.try(:userlist_user) || (user_method && model.try(user_method))
22
+ user_method && model.try(user_method)
23
23
  end
24
24
 
25
- def company
25
+ def default_company
26
26
  company_method = Userlist::Rails.find_reflection(config.relationship_model, config.company_model)&.name
27
27
 
28
- model.try(:userlist_company) || (company_method && model.try(company_method))
28
+ company_method && model.try(company_method)
29
29
  end
30
30
  end
31
31
  end
@@ -1,9 +1,12 @@
1
1
  require 'userlist/rails/transform'
2
+ require 'userlist/rails/transforms/has_relationships'
2
3
 
3
4
  module Userlist
4
5
  module Rails
5
6
  module Transforms
6
7
  class User < Userlist::Rails::Transform
8
+ include HasRelationships
9
+
7
10
  def self.attributes
8
11
  @attributes ||= [
9
12
  :identifier,
@@ -14,26 +17,37 @@ module Userlist
14
17
  ]
15
18
  end
16
19
 
17
- def identifier
18
- model.try(:userlist_identifier) || "#{model.class.name}-#{model.id}".parameterize
20
+ def default_identifier
21
+ "#{model.class.name}-#{model.id}".parameterize
19
22
  end
20
23
 
21
- def properties
22
- model.try(:userlist_properties) || {}
24
+ def default_properties
25
+ {}
23
26
  end
24
27
 
25
- def relationships
26
- relationships_method = Userlist::Rails.find_reflection(config.user_model, config.relationship_model)&.name
28
+ def default_email
29
+ model.try(:email)
30
+ end
27
31
 
28
- model.try(:userlist_relationships) || (relationships_method && model.try(relationships_method))
32
+ def default_signed_up_at
33
+ model.try(:created_at)
29
34
  end
30
35
 
31
- def email
32
- model.try(:userlist_email) || model.try(:email)
36
+ private
37
+
38
+ def build_relationship(record)
39
+ {
40
+ user: model,
41
+ company: record
42
+ }
33
43
  end
34
44
 
35
- def signed_up_at
36
- model.try(:created_at)
45
+ def relationship_from
46
+ config.user_model
47
+ end
48
+
49
+ def relationship_to
50
+ config.company_model
37
51
  end
38
52
  end
39
53
  end
@@ -1,5 +1,5 @@
1
1
  module Userlist
2
2
  module Rails
3
- VERSION = '0.5.0'.freeze
3
+ VERSION = '0.6.1'.freeze
4
4
  end
5
5
  end
@@ -23,7 +23,7 @@ Gem::Specification.new do |spec|
23
23
 
24
24
  spec.add_dependency 'activesupport', '>= 5.0'
25
25
  spec.add_dependency 'railties', '>= 5.0'
26
- spec.add_dependency 'userlist', '~> 0.5'
26
+ spec.add_dependency 'userlist', '~> 0.6'
27
27
 
28
28
  spec.add_development_dependency 'actionpack', '>= 5.0'
29
29
  spec.add_development_dependency 'activerecord', '>= 5.0'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: userlist-rails
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.0
4
+ version: 0.6.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Benedikt Deicke
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-01-22 00:00:00.000000000 Z
11
+ date: 2021-05-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -44,14 +44,14 @@ dependencies:
44
44
  requirements:
45
45
  - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: '0.5'
47
+ version: '0.6'
48
48
  type: :runtime
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: '0.5'
54
+ version: '0.6'
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: actionpack
57
57
  requirement: !ruby/object:Gem::Requirement
@@ -189,6 +189,7 @@ files:
189
189
  - lib/userlist/rails/tasks/userlist.rake
190
190
  - lib/userlist/rails/transform.rb
191
191
  - lib/userlist/rails/transforms/company.rb
192
+ - lib/userlist/rails/transforms/has_relationships.rb
192
193
  - lib/userlist/rails/transforms/relationship.rb
193
194
  - lib/userlist/rails/transforms/user.rb
194
195
  - lib/userlist/rails/version.rb