userlist 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.rubocop.yml +1 -1
- data/lib/userlist/config.rb +51 -11
- data/lib/userlist/logging.rb +11 -0
- data/lib/userlist/push/client.rb +9 -5
- data/lib/userlist/push/strategies/direct.rb +23 -0
- data/lib/userlist/push/strategies/null.rb +11 -0
- data/lib/userlist/push/strategies/threaded/worker.rb +51 -0
- data/lib/userlist/push/strategies/threaded.rb +28 -0
- data/lib/userlist/push/strategies.rb +17 -0
- data/lib/userlist/push.rb +62 -1
- data/lib/userlist/version.rb +1 -1
- data/lib/userlist.rb +18 -0
- data/userlist.gemspec +3 -2
- metadata +10 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: e36d328f443fa566c4e77f6d76bf7c3eb279677447d3451a56813868eef365c6
|
4
|
+
data.tar.gz: 42a788537faafae0cc66a8dcfd037c2ef7b2ca3ab78918922738e40e0b7c67b0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8ac7634b4f9a6059ee0c4c1c774be2d1b6d4ca2857eeceb4b4c6ef05ff7f070034780991cc196dc6f5ffe8f49f1f844a00f7ac614638f850d3e2e3a78ee644c8
|
7
|
+
data.tar.gz: 5a3154eeb7aa9368802961166c6fa89ab98c485d6c182d9a80d57d51edd0a09170da59237794d482fc5b1f470a19ce90f59ad5e297b864f6f633b028ea28fc3d
|
data/.rubocop.yml
CHANGED
data/lib/userlist/config.rb
CHANGED
@@ -1,39 +1,79 @@
|
|
1
1
|
module Userlist
|
2
2
|
class Config
|
3
3
|
DEFAULT_CONFIGURATION = {
|
4
|
-
push_key:
|
5
|
-
push_endpoint:
|
4
|
+
push_key: nil,
|
5
|
+
push_endpoint: 'https://push.userlist.io/',
|
6
|
+
push_strategy: :threaded,
|
7
|
+
log_level: :warn
|
6
8
|
}.freeze
|
7
9
|
|
8
10
|
def initialize(config_from_initialize = {})
|
9
|
-
@config =
|
11
|
+
@config = default_config
|
10
12
|
.merge(config_from_initialize)
|
11
13
|
.merge(config_from_environment)
|
12
14
|
end
|
13
15
|
|
14
|
-
|
16
|
+
def self.inherit(parent, config_from_arguments)
|
17
|
+
config = allocate
|
18
|
+
config.instance_variable_set(:@parent, parent)
|
19
|
+
config.instance_variable_set(:@config, config_from_arguments.to_hash)
|
20
|
+
config
|
21
|
+
end
|
22
|
+
|
23
|
+
def merge(other_config)
|
24
|
+
self.class.inherit(self, other_config)
|
25
|
+
end
|
26
|
+
|
27
|
+
def to_hash
|
28
|
+
config
|
29
|
+
end
|
30
|
+
|
31
|
+
def to_h
|
32
|
+
parent ? parent.to_h.merge(config) : to_hash
|
33
|
+
end
|
34
|
+
|
35
|
+
def ==(other)
|
36
|
+
config == other.config && parent == other.parent
|
37
|
+
end
|
38
|
+
|
39
|
+
protected
|
15
40
|
|
16
|
-
attr_reader :config
|
41
|
+
attr_reader :config, :parent
|
42
|
+
|
43
|
+
def default_config
|
44
|
+
DEFAULT_CONFIGURATION
|
45
|
+
end
|
17
46
|
|
18
47
|
def config_from_environment
|
19
|
-
|
48
|
+
default_config.keys.each_with_object({}) do |key, config|
|
20
49
|
value = ENV["USERLIST_#{key.to_s.upcase}"]
|
21
50
|
config[key] = value if value
|
22
51
|
end
|
23
52
|
end
|
24
53
|
|
25
|
-
def
|
54
|
+
def key?(key)
|
55
|
+
config.key?(key) || parent && parent.key?(key)
|
56
|
+
end
|
57
|
+
|
58
|
+
def [](key)
|
59
|
+
config[key] || parent && parent[key]
|
60
|
+
end
|
61
|
+
|
62
|
+
def []=(key, value)
|
63
|
+
config[key] = value
|
64
|
+
end
|
65
|
+
|
66
|
+
def respond_to_missing?(name, include_private = false)
|
26
67
|
name = name.to_s.sub(/=$/, '')
|
27
|
-
|
68
|
+
key?(name.to_sym) || super
|
28
69
|
end
|
29
70
|
|
30
71
|
def method_missing(name, *args, &block)
|
31
|
-
name = name.to_s
|
32
|
-
|
33
72
|
if respond_to_missing?(name)
|
73
|
+
name = name.to_s
|
34
74
|
method = name.match?(/=$/) ? :[]= : :[]
|
35
75
|
name = name.sub(/=$/, '').to_sym
|
36
|
-
|
76
|
+
send(method, name, *args, &block)
|
37
77
|
else
|
38
78
|
super
|
39
79
|
end
|
data/lib/userlist/push/client.rb
CHANGED
@@ -4,10 +4,12 @@ require 'net/http'
|
|
4
4
|
require 'openssl'
|
5
5
|
|
6
6
|
module Userlist
|
7
|
-
|
7
|
+
class Push
|
8
8
|
class Client
|
9
|
-
|
10
|
-
|
9
|
+
include Userlist::Logging
|
10
|
+
|
11
|
+
def initialize(config = {})
|
12
|
+
@config = Userlist.config.merge(config)
|
11
13
|
end
|
12
14
|
|
13
15
|
def post(endpoint, payload = {})
|
@@ -31,13 +33,15 @@ module Userlist
|
|
31
33
|
end
|
32
34
|
end
|
33
35
|
|
34
|
-
def request(
|
35
|
-
request = Net::HTTP::Post.new(
|
36
|
+
def request(path, payload = {})
|
37
|
+
request = Net::HTTP::Post.new(path)
|
36
38
|
request['Accept'] = 'application/json'
|
37
39
|
request['Authorization'] = "Push #{token}"
|
38
40
|
request['Content-Type'] = 'application/json; charset=UTF-8'
|
39
41
|
request.body = JSON.dump(payload)
|
40
42
|
|
43
|
+
logger.debug "Sending #{request.method} to #{URI.join(endpoint, request.path)} with body #{request.body}"
|
44
|
+
|
41
45
|
http.request(request)
|
42
46
|
end
|
43
47
|
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Userlist
|
2
|
+
class Push
|
3
|
+
module Strategies
|
4
|
+
class Direct
|
5
|
+
def initialize(config = {})
|
6
|
+
@config = Userlist.config.merge(config)
|
7
|
+
end
|
8
|
+
|
9
|
+
def call(*args)
|
10
|
+
client.public_send(*args)
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
attr_reader :config
|
16
|
+
|
17
|
+
def client
|
18
|
+
@client ||= Userlist::Push::Client.new(config)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module Userlist
|
2
|
+
class Push
|
3
|
+
module Strategies
|
4
|
+
class Threaded
|
5
|
+
class Worker
|
6
|
+
include Userlist::Logging
|
7
|
+
|
8
|
+
MAX_WORKER_TIMEOUT = 30
|
9
|
+
|
10
|
+
def initialize(queue, config = {})
|
11
|
+
@queue = queue
|
12
|
+
@config = Userlist.config.merge(config)
|
13
|
+
@thread = Thread.new { run }
|
14
|
+
@thread.abort_on_exception = true
|
15
|
+
end
|
16
|
+
|
17
|
+
def run
|
18
|
+
logger.info 'Starting worker thread...'
|
19
|
+
|
20
|
+
loop do
|
21
|
+
begin
|
22
|
+
method, url, payload = *queue.pop
|
23
|
+
break if method == :stop
|
24
|
+
|
25
|
+
client.public_send(method, url, payload)
|
26
|
+
rescue StandardError => exception
|
27
|
+
logger.error "Failed to deliver payload: [#{exception.class.name}] #{exception.message}"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
logger.info "Worker thread exited with #{queue.size} tasks still in the queue..."
|
32
|
+
end
|
33
|
+
|
34
|
+
def stop
|
35
|
+
logger.info 'Stopping worker thread...'
|
36
|
+
queue.push([:stop])
|
37
|
+
thread.join(MAX_WORKER_TIMEOUT)
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
attr_reader :queue, :config, :thread
|
43
|
+
|
44
|
+
def client
|
45
|
+
@client ||= Userlist::Push::Client.new(config)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'userlist/push/strategies/threaded/worker'
|
2
|
+
|
3
|
+
module Userlist
|
4
|
+
class Push
|
5
|
+
module Strategies
|
6
|
+
class Threaded
|
7
|
+
def initialize(config = {})
|
8
|
+
@queue = Queue.new
|
9
|
+
@worker = Worker.new(queue, config)
|
10
|
+
|
11
|
+
at_exit { stop_worker }
|
12
|
+
end
|
13
|
+
|
14
|
+
def call(*args)
|
15
|
+
queue.push(args)
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
attr_reader :queue, :worker
|
21
|
+
|
22
|
+
def stop_worker
|
23
|
+
worker && worker.stop
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'userlist/push/strategies/null'
|
2
|
+
require 'userlist/push/strategies/direct'
|
3
|
+
require 'userlist/push/strategies/threaded'
|
4
|
+
|
5
|
+
module Userlist
|
6
|
+
class Push
|
7
|
+
module Strategies
|
8
|
+
def self.strategy_for(strategy, config = {})
|
9
|
+
strategy = Userlist::Push::Strategies.const_get(strategy.to_s.capitalize) if strategy.is_a?(Symbol) || strategy.is_a?(String)
|
10
|
+
|
11
|
+
strategy = strategy.new(config) if strategy.respond_to?(:new)
|
12
|
+
|
13
|
+
strategy
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
data/lib/userlist/push.rb
CHANGED
@@ -1,6 +1,67 @@
|
|
1
1
|
require 'userlist/push/client'
|
2
|
+
require 'userlist/push/strategies'
|
2
3
|
|
3
4
|
module Userlist
|
4
|
-
|
5
|
+
class Push
|
6
|
+
class << self
|
7
|
+
[:event, :track, :user, :identify, :company].each do |method|
|
8
|
+
define_method(method) { |*args| default_push_instance.send(method, *args) }
|
9
|
+
end
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
def default_push_instance
|
14
|
+
@default_push_instance ||= new
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def initialize(config = {})
|
19
|
+
@config = Userlist.config.merge(config)
|
20
|
+
@mutex = Mutex.new
|
21
|
+
end
|
22
|
+
|
23
|
+
def event(payload = {})
|
24
|
+
with_mutex do
|
25
|
+
raise ArgumentError, 'Missing required payload hash' unless payload
|
26
|
+
raise ArgumentError, 'Missing required parameter :name' unless payload[:name]
|
27
|
+
raise ArgumentError, 'Missing required parameter :user' unless payload[:user]
|
28
|
+
|
29
|
+
payload[:occured_at] ||= Time.now
|
30
|
+
|
31
|
+
strategy.call(:post, '/events', payload)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
alias track event
|
35
|
+
|
36
|
+
def user(payload = {})
|
37
|
+
with_mutex do
|
38
|
+
raise ArgumentError, 'Missing required payload hash' unless payload
|
39
|
+
raise ArgumentError, 'Missing required parameter :identifier' unless payload[:identifier]
|
40
|
+
|
41
|
+
strategy.call(:post, '/users', payload)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
alias identify user
|
45
|
+
|
46
|
+
def company(payload = {})
|
47
|
+
with_mutex do
|
48
|
+
raise ArgumentError, 'Missing required payload hash' unless payload
|
49
|
+
raise ArgumentError, 'Missing required parameter :identifier' unless payload[:identifier]
|
50
|
+
|
51
|
+
strategy.call(:post, '/companies', payload)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
|
57
|
+
attr_reader :config
|
58
|
+
|
59
|
+
def strategy
|
60
|
+
@strategy ||= Userlist::Push::Strategies.strategy_for(config.push_strategy, config)
|
61
|
+
end
|
62
|
+
|
63
|
+
def with_mutex(&block)
|
64
|
+
@mutex.synchronize(&block)
|
65
|
+
end
|
5
66
|
end
|
6
67
|
end
|
data/lib/userlist/version.rb
CHANGED
data/lib/userlist.rb
CHANGED
@@ -1,5 +1,8 @@
|
|
1
|
+
require 'logger'
|
2
|
+
|
1
3
|
require 'userlist/version'
|
2
4
|
require 'userlist/config'
|
5
|
+
require 'userlist/logging'
|
3
6
|
require 'userlist/push'
|
4
7
|
|
5
8
|
module Userlist
|
@@ -7,5 +10,20 @@ module Userlist
|
|
7
10
|
def config
|
8
11
|
@config ||= Userlist::Config.new
|
9
12
|
end
|
13
|
+
|
14
|
+
def logger
|
15
|
+
@logger ||= begin
|
16
|
+
logger = Logger.new(STDOUT)
|
17
|
+
logger.progname = 'userlist'
|
18
|
+
logger.level = config.log_level
|
19
|
+
logger
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def configure
|
24
|
+
yield config
|
25
|
+
end
|
26
|
+
|
27
|
+
attr_writer :logger
|
10
28
|
end
|
11
29
|
end
|
data/userlist.gemspec
CHANGED
@@ -1,5 +1,4 @@
|
|
1
|
-
|
2
|
-
lib = File.expand_path('../lib', __FILE__)
|
1
|
+
lib = File.expand_path('lib', __dir__)
|
3
2
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
3
|
require 'userlist/version'
|
5
4
|
|
@@ -20,6 +19,8 @@ Gem::Specification.new do |spec|
|
|
20
19
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
21
20
|
spec.require_paths = ['lib']
|
22
21
|
|
22
|
+
spec.required_ruby_version = '>= 2.1'
|
23
|
+
|
23
24
|
spec.add_development_dependency 'bundler', '~> 1.15'
|
24
25
|
spec.add_development_dependency 'rake', '~> 10.0'
|
25
26
|
spec.add_development_dependency 'rspec', '~> 3.0'
|
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.
|
4
|
+
version: 0.2.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: 2018-
|
11
|
+
date: 2018-11-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -87,8 +87,14 @@ files:
|
|
87
87
|
- bin/setup
|
88
88
|
- lib/userlist.rb
|
89
89
|
- lib/userlist/config.rb
|
90
|
+
- lib/userlist/logging.rb
|
90
91
|
- lib/userlist/push.rb
|
91
92
|
- lib/userlist/push/client.rb
|
93
|
+
- lib/userlist/push/strategies.rb
|
94
|
+
- lib/userlist/push/strategies/direct.rb
|
95
|
+
- lib/userlist/push/strategies/null.rb
|
96
|
+
- lib/userlist/push/strategies/threaded.rb
|
97
|
+
- lib/userlist/push/strategies/threaded/worker.rb
|
92
98
|
- lib/userlist/version.rb
|
93
99
|
- userlist.gemspec
|
94
100
|
homepage: http://github.com/userlistio/userlist-ruby
|
@@ -103,7 +109,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
103
109
|
requirements:
|
104
110
|
- - ">="
|
105
111
|
- !ruby/object:Gem::Version
|
106
|
-
version: '
|
112
|
+
version: '2.1'
|
107
113
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
108
114
|
requirements:
|
109
115
|
- - ">="
|
@@ -111,7 +117,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
111
117
|
version: '0'
|
112
118
|
requirements: []
|
113
119
|
rubyforge_project:
|
114
|
-
rubygems_version: 2.
|
120
|
+
rubygems_version: 2.7.3
|
115
121
|
signing_key:
|
116
122
|
specification_version: 4
|
117
123
|
summary: Ruby wrapper for the Userlist.io API
|