userlist 0.7.2 → 0.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ee5f47b48e2bcc0babdbb7bcc85e84e5b197dacefb777eb19764d6f23e78e682
4
- data.tar.gz: 53caac6db3e462e9fe280541a7249da39e821a6a8bed7c70373131bebe9a6e19
3
+ metadata.gz: 4d6e93ef4ff51c272a689c07261fd423941a7244bec1cedf84af7207cd702816
4
+ data.tar.gz: 6c424b3183ec05af8a620dd39dea582bd097fc1514e800f8cd2b63aab342b90f
5
5
  SHA512:
6
- metadata.gz: 687b4ff1d89c5c98814fc89fe7cb8097577bd0cea63138f40e194eb47d6d6a2807b624c9dd15f2b64868f0473f1baaf268b6f7cb3945b713c20d00150b71739c
7
- data.tar.gz: 0b72bc8a28a6cba596c5afaff369e62b26cc8a84347f0059f68859c02df270562663c9e7faa8ddbb89a7053cf6d811d7dfcafa75f2f9233c27a36aebd72e4b4a
6
+ metadata.gz: f8ef2403697c2a9369f0b74737a5429cd0f650c4c22f1e09ab18d024dc7750622032077eb1fb392611ee1fa1907ea48b8e2d2aad2afbf6246fe3eb96c7f2fddd
7
+ data.tar.gz: e2578be25dd0be0db2d721d900f14910f238a72554918319ecad8ba16d3fffaac5da982b6e4c306c4af6bc540f12baeea71c6e7a4e9a0c4418a150028cb38fee
@@ -5,14 +5,12 @@ jobs:
5
5
  strategy:
6
6
  matrix:
7
7
  ruby:
8
- - 2.4
9
- - 2.5
10
- - 2.6
11
- - 2.7
12
8
  - 3.0
9
+ - 3.1
10
+ - 3.2
13
11
  runs-on: ubuntu-latest
14
12
  steps:
15
- - uses: actions/checkout@v2
13
+ - uses: actions/checkout@v3
16
14
  - uses: ruby/setup-ruby@v1
17
15
  with:
18
16
  ruby-version: ${{ matrix.ruby }}
data/.rubocop.yml CHANGED
@@ -51,6 +51,9 @@ Style/ClassAndModuleChildren:
51
51
  Layout/MultilineMethodCallIndentation:
52
52
  EnforcedStyle: indented
53
53
 
54
+ Layout/SpaceAroundOperators:
55
+ EnforcedStyleForExponentOperator: space
56
+
54
57
  Style/SymbolArray:
55
58
  Enabled: false
56
59
 
data/CHANGELOG.md ADDED
@@ -0,0 +1,84 @@
1
+ # Userlist for Ruby Changelog
2
+
3
+ ## v0.8.1 (2023-11-30)
4
+
5
+ - Fixes issue with customizability of push? and delete? methods on events and relationships
6
+
7
+ ## v0.8.0 (2023-08-02)
8
+
9
+ - Adds a ActiveJob push strategy
10
+ - Adds support for retries of failed requests
11
+ - Adds response logging to the push client
12
+ - Adds support for payloads on delete requests
13
+
14
+ ## v0.7.2 (2022-10-20)
15
+
16
+ - Improves connection handling between requests
17
+ - Improves handling of arguments passed to Sidekiq
18
+
19
+ ## v0.7.1 (2022-02-10)
20
+
21
+ - Fixes issue with Sidekiq payloads (#4)
22
+
23
+ ## v0.7.0 (2022-02-02)
24
+
25
+ - Allows users without an identifier but with an email address
26
+
27
+ ## v0.6.0 (2021-05-14)
28
+
29
+ - Automatically manage one side of the relationships
30
+ - Always requires a user and a company on relationships
31
+
32
+ ## v0.5.0 (2021-01-22)
33
+
34
+ - Adds support for relationships
35
+ - Adds support for company events
36
+ - Adds support for skipping certain operations
37
+ - Adds lazy lookup for push strategies
38
+ - Adds a Sidekiq push strategy
39
+ - Allow numerics as identifiers
40
+ - Improves Userlist::Token to work with more than just strings
41
+ - Improves the way resources are serialized
42
+ - Improves JSON serialization
43
+ - Replaces userlist.io with userlist.com
44
+ - Require at least Ruby 2.4
45
+
46
+ ## v0.4.1 (2020-03-16)
47
+
48
+ - Fixes a problem when configuring the client (#1)
49
+
50
+ ## v0.4.0 (2020-03-06)
51
+
52
+ - Adds improved error messages for configuration errors
53
+ - Adds support for user token generation
54
+ - Require at least Ruby 2.3
55
+
56
+ ## v0.3.0 (2019-06-27)
57
+
58
+ - Adds additional aliases for the create method
59
+ - Adds resource models for User, Company, and Event
60
+ - Adds a more flexible interface to the push client
61
+ - Adds more HTTP methods to the push client
62
+
63
+ ## v0.2.2 (2019-03-18)
64
+
65
+ - Adds support for Ruby 2.2
66
+
67
+ ## v0.2.1 (2019-03-13)
68
+
69
+ - Adds support for Ruby 2.3
70
+
71
+ ## v0.2.0 (2018-11-21)
72
+
73
+ - Adds the ability to adjust the configuration
74
+ - Adds convenience class methods to Userlist::Push
75
+ - Adds track and identify aliases for event and user push methods
76
+ - Adds null strategy that discards everything for testing purposes
77
+ - Adds logging support
78
+ - Adds threaded push strategy to deliver requests without blocking the main thread
79
+ - Adds Userlist::Push as a nicer interface to the push endpoint
80
+ - Require at least Ruby 2.1
81
+
82
+ ## v0.1.0 (2018-01-18)
83
+
84
+ - Initial release
data/Gemfile CHANGED
@@ -6,6 +6,8 @@ gemspec
6
6
 
7
7
  gem 'guard-rspec', '~> 4.7'
8
8
  gem 'guard-rubocop', '~> 1.3'
9
- gem 'rubocop', '~> 0.68'
9
+ gem 'rubocop', '~> 1.45'
10
10
 
11
11
  gem 'sidekiq'
12
+ gem 'activejob'
13
+ gem 'uri'
data/README.md CHANGED
@@ -44,15 +44,15 @@ end
44
44
 
45
45
  The possible configuration values are listed in the table below.
46
46
 
47
- | Name | Default value | Description |
48
- | ----------------------- | ---------------------------- | --------------------------------------------------------------------------------------------------------------------------- |
49
- | `push_key` | `nil` | The push key for your account. See [Push API settings](https://app.userlist.com/settings/push). |
50
- | `push_id` | `nil` | The push id for your account. See [Push API settings](https://app.userlist.com/settings/push). |
51
- | `push_endpoint` | `https://push.userlist.com/` | The HTTP endpoint that the library will send data to. |
52
- | `push_strategy` | `:threaded` | The strategy to use to send data to the HTTP endpoint. Possible values are `:threaded`, `:sidekiq`, `:direct`, and `:null`. |
53
- | `push_strategy_options` | `{}` | Any additional options for the push strategy. |
54
- | `log_level` | `:warn` | The log level for Userlist related log messages. Possible values are `:debug`, `:error`, `:fatal`, `:info`, and `:warn` |
55
- | `token_lifetime` | `3600` | The lifetime of generated in-app messages tokens in seconds |
47
+ | Name | Default value | Description |
48
+ | ----------------------- | ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
49
+ | `push_key` | `nil` | The push key for your account. See [Push API settings](https://app.userlist.com/settings/push). |
50
+ | `push_id` | `nil` | The push id for your account. See [Push API settings](https://app.userlist.com/settings/push). |
51
+ | `push_endpoint` | `https://push.userlist.com/` | The HTTP endpoint that the library will send data to. |
52
+ | `push_strategy` | `:threaded` | The strategy to use to send data to the HTTP endpoint. Possible values are `:threaded`, `:sidekiq`, `:active_job`, `:direct`, and `:null`. |
53
+ | `push_strategy_options` | `{}` | Any additional options for the push strategy. |
54
+ | `log_level` | `:warn` | The log level for Userlist related log messages. Possible values are `:debug`, `:error`, `:fatal`, `:info`, and `:warn` |
55
+ | `token_lifetime` | `3600` | The lifetime of generated in-app messages tokens in seconds |
56
56
 
57
57
  ### Disabling in development and test environments
58
58
 
@@ -53,7 +53,7 @@ module Userlist
53
53
 
54
54
  def config_from_environment
55
55
  default_config.keys.each_with_object({}) do |key, config|
56
- value = ENV["USERLIST_#{key.to_s.upcase}"]
56
+ value = ENV.fetch("USERLIST_#{key.to_s.upcase}", nil)
57
57
  config[key] = value if value
58
58
  end
59
59
  end
@@ -27,8 +27,8 @@ module Userlist
27
27
  request(Net::HTTP::Put, endpoint, payload)
28
28
  end
29
29
 
30
- def delete(endpoint)
31
- request(Net::HTTP::Delete, endpoint)
30
+ def delete(endpoint, payload = nil)
31
+ request(Net::HTTP::Delete, endpoint, payload)
32
32
  end
33
33
 
34
34
  private
@@ -58,7 +58,11 @@ module Userlist
58
58
  logger.debug "Sending #{request.method} to #{URI.join(endpoint, request.path)} with body #{request.body}"
59
59
 
60
60
  http.start unless http.started?
61
- http.request(request)
61
+ response = http.request(request)
62
+
63
+ logger.debug "Recieved #{response.code} #{response.message} with body #{response.body}"
64
+
65
+ response
62
66
  end
63
67
 
64
68
  def endpoint
@@ -19,7 +19,7 @@ module Userlist
19
19
  end
20
20
 
21
21
  def push?
22
- (user.nil? || user.push?) && (company.nil? || company.push?)
22
+ super && (user.nil? || user.push?) && (company.nil? || company.push?)
23
23
  end
24
24
  end
25
25
  end
@@ -23,7 +23,7 @@ module Userlist
23
23
  end
24
24
 
25
25
  def push?
26
- user&.push? && company&.push?
26
+ super && user&.push? && company&.push?
27
27
  end
28
28
  end
29
29
  end
@@ -33,9 +33,9 @@ module Userlist
33
33
  relationships[name.to_sym] = { type: type }
34
34
 
35
35
  generated_methods.class_eval <<-RUBY, __FILE__, __LINE__ + 1
36
- def #{name}
37
- #{type}.from_payload(payload[:#{name}], config)
38
- end
36
+ def #{name} # def company
37
+ #{type}.from_payload(payload[:#{name}], config) # Company.from_payload(payload[:company], config)
38
+ end # end
39
39
  RUBY
40
40
  end
41
41
 
@@ -43,11 +43,11 @@ module Userlist
43
43
  relationships[name.to_sym] = options
44
44
 
45
45
  generated_methods.class_eval <<-RUBY, __FILE__, __LINE__ + 1
46
- def #{name}
47
- relationship = self.class.relationships[:#{name}]
48
-
49
- ResourceCollection.new(payload[:#{name}], relationship, self, config)
50
- end
46
+ def #{name} # def companies
47
+ relationship = self.class.relationships[:#{name}] # relationship = self.class.relationships[:companies]
48
+ #
49
+ ResourceCollection.new(payload[:#{name}], relationship, self, config) # ResourceCollection.new(payload[:companies], relationship, self, config)
50
+ end #
51
51
  RUBY
52
52
  end
53
53
 
@@ -0,0 +1,16 @@
1
+ require 'active_job'
2
+
3
+ module Userlist
4
+ class Push
5
+ module Strategies
6
+ class ActiveJob
7
+ class Worker < ::ActiveJob::Base
8
+ def perform(method, *args)
9
+ client = Userlist::Push::Client.new
10
+ client.public_send(method, *args)
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,47 @@
1
+ require 'active_job'
2
+
3
+ require 'userlist/push/strategies/active_job/worker'
4
+
5
+ module Userlist
6
+ class Push
7
+ module Strategies
8
+ class ActiveJob
9
+ def initialize(config = {})
10
+ @config = Userlist.config.merge(config)
11
+ end
12
+
13
+ def call(*args)
14
+ options = default_options.merge(self.options)
15
+
16
+ worker_name = options.delete(:class)
17
+ worker_class = Object.const_get(worker_name)
18
+ worker_class
19
+ .set(options)
20
+ .perform_later(*normalize(args))
21
+ end
22
+
23
+ private
24
+
25
+ attr_reader :config
26
+
27
+ def options
28
+ @options ||= begin
29
+ options = config.push_strategy_options || {}
30
+ options.each_with_object({}) { |(k, v), h| h[k.to_sym] = v }
31
+ end
32
+ end
33
+
34
+ def default_options
35
+ {
36
+ class: 'Userlist::Push::Strategies::ActiveJob::Worker',
37
+ queue: 'default'
38
+ }
39
+ end
40
+
41
+ def normalize(args)
42
+ JSON.parse(JSON.generate(args))
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -7,7 +7,7 @@ module Userlist
7
7
  end
8
8
 
9
9
  def call(*args)
10
- client.public_send(*args)
10
+ retryable.attempt { client.public_send(*args) }
11
11
  end
12
12
 
13
13
  private
@@ -17,6 +17,14 @@ module Userlist
17
17
  def client
18
18
  @client ||= Userlist::Push::Client.new(config)
19
19
  end
20
+
21
+ def retryable
22
+ @retryable ||= Userlist::Retryable.new do |response|
23
+ status = response.code.to_i
24
+
25
+ status >= 500 || status == 429
26
+ end
27
+ end
20
28
  end
21
29
  end
22
30
  end
@@ -22,7 +22,7 @@ module Userlist
22
22
  method, *args = *queue.pop
23
23
  break if method == :stop
24
24
 
25
- client.public_send(method, *args)
25
+ retryable.attempt { client.public_send(method, *args) }
26
26
  rescue StandardError => e
27
27
  logger.error "Failed to deliver payload: [#{e.class.name}] #{e.message}"
28
28
  end
@@ -44,6 +44,14 @@ module Userlist
44
44
  def client
45
45
  @client ||= Userlist::Push::Client.new(config)
46
46
  end
47
+
48
+ def retryable
49
+ @retryable ||= Userlist::Retryable.new do |response|
50
+ status = response.code.to_i
51
+
52
+ status >= 500 || status == 429
53
+ end
54
+ end
47
55
  end
48
56
  end
49
57
  end
@@ -12,13 +12,16 @@ module Userlist
12
12
  return strategy unless strategy.is_a?(Symbol) || strategy.is_a?(String)
13
13
 
14
14
  require_strategy(strategy)
15
- const_get(strategy.to_s.capitalize, false)
15
+
16
+ class_name = classify_strategy(strategy)
17
+ const_get(class_name, false)
16
18
  end
17
19
 
18
20
  def self.strategy_defined?(strategy)
19
21
  return true unless strategy.is_a?(Symbol) || strategy.is_a?(String)
20
22
 
21
- const_defined?(strategy.to_s.capitalize, false)
23
+ class_name = classify_strategy(strategy)
24
+ const_defined?(class_name, false)
22
25
  end
23
26
 
24
27
  def self.require_strategy(strategy)
@@ -26,6 +29,11 @@ module Userlist
26
29
 
27
30
  require("userlist/push/strategies/#{strategy}") unless strategy_defined?(strategy)
28
31
  end
32
+
33
+ def self.classify_strategy(strategy)
34
+ strategy.to_s.split('_').map(&:capitalize).join
35
+ end
36
+ private_class_method :classify_strategy
29
37
  end
30
38
  end
31
39
  end
@@ -0,0 +1,49 @@
1
+ module Userlist
2
+ class Retryable
3
+ include Userlist::Logging
4
+
5
+ RETRIES = 10
6
+ DELAY = 100
7
+ MULTIPLIER = 2
8
+ MAX_DELAY = 10_000
9
+
10
+ def initialize(retries: RETRIES, delay: DELAY, max_delay: MAX_DELAY, multiplier: MULTIPLIER, &retry_check)
11
+ @retries = retries
12
+ @delay = delay
13
+ @max_delay = max_delay
14
+ @multiplier = multiplier
15
+ @retry_check = retry_check
16
+ end
17
+
18
+ def retry?(value)
19
+ @retry_check.call(value)
20
+ end
21
+
22
+ def attempt
23
+ (0..@retries).each do |attempt|
24
+ if attempt.positive?
25
+ milliseconds = delay(attempt)
26
+ logger.debug "Retrying in #{milliseconds}ms, #{@retries - attempt} retries left"
27
+ sleep(milliseconds / 1000.0)
28
+ end
29
+
30
+ result = yield
31
+
32
+ return result unless retry?(result)
33
+ end
34
+
35
+ logger.debug 'Retries exhausted, giving up'
36
+
37
+ nil
38
+ end
39
+
40
+ private
41
+
42
+ def delay(attempt)
43
+ [
44
+ @delay * (@multiplier ** attempt),
45
+ @max_delay
46
+ ].min
47
+ end
48
+ end
49
+ end
@@ -1,3 +1,3 @@
1
1
  module Userlist
2
- VERSION = '0.7.2'.freeze
2
+ VERSION = '0.8.1'.freeze
3
3
  end
data/lib/userlist.rb CHANGED
@@ -3,6 +3,7 @@ require 'logger'
3
3
  require 'userlist/version'
4
4
  require 'userlist/config'
5
5
  require 'userlist/logging'
6
+ require 'userlist/retryable'
6
7
  require 'userlist/push'
7
8
  require 'userlist/token'
8
9
 
data/userlist.gemspec CHANGED
@@ -23,7 +23,9 @@ Gem::Specification.new do |spec|
23
23
 
24
24
  spec.add_development_dependency 'bundler', '>= 1.15'
25
25
  spec.add_development_dependency 'jwt', '~> 2.2'
26
- spec.add_development_dependency 'rake', '~> 12.3', '>= 12.3.3'
26
+ spec.add_development_dependency 'rake', '~> 13.0'
27
27
  spec.add_development_dependency 'rspec', '~> 3.0'
28
- spec.add_development_dependency 'webmock', '~> 1.22'
28
+ spec.add_development_dependency 'webmock', '~> 3.18'
29
+
30
+ spec.metadata = { 'rubygems_mfa_required' => 'true' }
29
31
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: userlist
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.2
4
+ version: 0.8.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: 2022-10-20 00:00:00.000000000 Z
11
+ date: 2023-11-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -44,20 +44,14 @@ dependencies:
44
44
  requirements:
45
45
  - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: '12.3'
48
- - - ">="
49
- - !ruby/object:Gem::Version
50
- version: 12.3.3
47
+ version: '13.0'
51
48
  type: :development
52
49
  prerelease: false
53
50
  version_requirements: !ruby/object:Gem::Requirement
54
51
  requirements:
55
52
  - - "~>"
56
53
  - !ruby/object:Gem::Version
57
- version: '12.3'
58
- - - ">="
59
- - !ruby/object:Gem::Version
60
- version: 12.3.3
54
+ version: '13.0'
61
55
  - !ruby/object:Gem::Dependency
62
56
  name: rspec
63
57
  requirement: !ruby/object:Gem::Requirement
@@ -78,14 +72,14 @@ dependencies:
78
72
  requirements:
79
73
  - - "~>"
80
74
  - !ruby/object:Gem::Version
81
- version: '1.22'
75
+ version: '3.18'
82
76
  type: :development
83
77
  prerelease: false
84
78
  version_requirements: !ruby/object:Gem::Requirement
85
79
  requirements:
86
80
  - - "~>"
87
81
  - !ruby/object:Gem::Version
88
- version: '1.22'
82
+ version: '3.18'
89
83
  description:
90
84
  email:
91
85
  - benedikt@userlist.com
@@ -97,6 +91,7 @@ files:
97
91
  - ".gitignore"
98
92
  - ".rspec"
99
93
  - ".rubocop.yml"
94
+ - CHANGELOG.md
100
95
  - CODE_OF_CONDUCT.md
101
96
  - Gemfile
102
97
  - Guardfile
@@ -120,6 +115,8 @@ files:
120
115
  - lib/userlist/push/resource_collection.rb
121
116
  - lib/userlist/push/serializer.rb
122
117
  - lib/userlist/push/strategies.rb
118
+ - lib/userlist/push/strategies/active_job.rb
119
+ - lib/userlist/push/strategies/active_job/worker.rb
123
120
  - lib/userlist/push/strategies/direct.rb
124
121
  - lib/userlist/push/strategies/null.rb
125
122
  - lib/userlist/push/strategies/sidekiq.rb
@@ -127,13 +124,15 @@ files:
127
124
  - lib/userlist/push/strategies/threaded.rb
128
125
  - lib/userlist/push/strategies/threaded/worker.rb
129
126
  - lib/userlist/push/user.rb
127
+ - lib/userlist/retryable.rb
130
128
  - lib/userlist/token.rb
131
129
  - lib/userlist/version.rb
132
130
  - userlist.gemspec
133
131
  homepage: http://github.com/userlistio/userlist-ruby
134
132
  licenses:
135
133
  - MIT
136
- metadata: {}
134
+ metadata:
135
+ rubygems_mfa_required: 'true'
137
136
  post_install_message:
138
137
  rdoc_options: []
139
138
  require_paths:
@@ -149,7 +148,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
149
148
  - !ruby/object:Gem::Version
150
149
  version: '0'
151
150
  requirements: []
152
- rubygems_version: 3.3.7
151
+ rubygems_version: 3.4.21
153
152
  signing_key:
154
153
  specification_version: 4
155
154
  summary: Ruby wrapper for the Userlist API