userlist 0.7.1 → 0.8.0

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: 93b343c10d2614536619adc6ab7ecfa1ceb1f2a778e7748425f2fb7df3316a21
4
- data.tar.gz: 55ad33ee9a9aef2ee81b0ffb507016bb25a8fc7844205520c7f0a48ec0de9f76
3
+ metadata.gz: ee98b5cd1e17b91ed12fce427d0c87ec4593da547349015ca4ed9272dba73100
4
+ data.tar.gz: 3ec9d567b7c8c1894841483cd136c76db4e1a667eac7101541fcd3d232b20a8f
5
5
  SHA512:
6
- metadata.gz: 0113f93c4b9a64c559f052900edef4677bd0ec0000e97b68daebb81b50614d7df83076d0e668d1cd8f2a65fa973600b92589db0a622ab45d8f5797cbea1e3bef
7
- data.tar.gz: 8dba2559f0660836a11ef6b5b709f76399f1e8b7b2dfdfd4d4caf54ffac2c0cd6f7775bcd7ee801c4dc6bbd10698cd208b4d4b4a672f9636d846265553767f4c
6
+ metadata.gz: 68340e08984d801834f1f853102119df452da6455bdca3b1eedf0d743d829c750fc81e234ef661f964402e44173b9951a891f15cb9ed0665929d525fbd0b03df
7
+ data.tar.gz: 1d6e7976d2e5486506306b2623135b4addf4cb7c6b6f3c3d81e30fde6d4fb733eb9d289027812ae6390d82b0b7cf4a5742104b0496d88372b0e720a8ed89402d
@@ -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/.rspec CHANGED
@@ -1,2 +1,4 @@
1
1
  --format documentation
2
+ --require spec_helper
3
+ --order random
2
4
  --color
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/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
@@ -57,7 +57,12 @@ module Userlist
57
57
 
58
58
  logger.debug "Sending #{request.method} to #{URI.join(endpoint, request.path)} with body #{request.body}"
59
59
 
60
- http.request(request)
60
+ http.start unless http.started?
61
+ response = http.request(request)
62
+
63
+ logger.debug "Recieved #{response.code} #{response.message} with body #{response.body}"
64
+
65
+ response
61
66
  end
62
67
 
63
68
  def endpoint
@@ -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.1'.freeze
2
+ VERSION = '0.8.0'.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
 
@@ -51,6 +52,10 @@ module Userlist
51
52
  yield config
52
53
  end
53
54
 
55
+ def reset!
56
+ @config = nil
57
+ end
58
+
54
59
  attr_writer :logger
55
60
  end
56
61
  end
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.1
4
+ version: 0.8.0
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-02-10 00:00:00.000000000 Z
11
+ date: 2023-08-02 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
@@ -120,6 +114,8 @@ files:
120
114
  - lib/userlist/push/resource_collection.rb
121
115
  - lib/userlist/push/serializer.rb
122
116
  - lib/userlist/push/strategies.rb
117
+ - lib/userlist/push/strategies/active_job.rb
118
+ - lib/userlist/push/strategies/active_job/worker.rb
123
119
  - lib/userlist/push/strategies/direct.rb
124
120
  - lib/userlist/push/strategies/null.rb
125
121
  - lib/userlist/push/strategies/sidekiq.rb
@@ -127,13 +123,15 @@ files:
127
123
  - lib/userlist/push/strategies/threaded.rb
128
124
  - lib/userlist/push/strategies/threaded/worker.rb
129
125
  - lib/userlist/push/user.rb
126
+ - lib/userlist/retryable.rb
130
127
  - lib/userlist/token.rb
131
128
  - lib/userlist/version.rb
132
129
  - userlist.gemspec
133
130
  homepage: http://github.com/userlistio/userlist-ruby
134
131
  licenses:
135
132
  - MIT
136
- metadata: {}
133
+ metadata:
134
+ rubygems_mfa_required: 'true'
137
135
  post_install_message:
138
136
  rdoc_options: []
139
137
  require_paths:
@@ -149,7 +147,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
149
147
  - !ruby/object:Gem::Version
150
148
  version: '0'
151
149
  requirements: []
152
- rubygems_version: 3.2.32
150
+ rubygems_version: 3.4.6
153
151
  signing_key:
154
152
  specification_version: 4
155
153
  summary: Ruby wrapper for the Userlist API