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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 9534a94215f86a6e4da9add6729d0dfdc04f2d37
4
- data.tar.gz: 8d6a8b22aaeca9dc9ab99b4d4f567929c797da47
2
+ SHA256:
3
+ metadata.gz: e36d328f443fa566c4e77f6d76bf7c3eb279677447d3451a56813868eef365c6
4
+ data.tar.gz: 42a788537faafae0cc66a8dcfd037c2ef7b2ca3ab78918922738e40e0b7c67b0
5
5
  SHA512:
6
- metadata.gz: 60533319f67189405db69dc774ea8135408386d4305fdd82d1c0cc9c864a28f4620ab16a62947f571cfdcd52379a3fa3ee9d36c4d5033ce39cb3c0aa5d46fbbd
7
- data.tar.gz: 6b9870124cc409d969fbf0a5c374de2956184d7f47a5b8b1c9b79107af0753de8c3dff663c5b697941262f0fc2507c96a7335d77fc24a2a4a1397d453af0ca97
6
+ metadata.gz: 8ac7634b4f9a6059ee0c4c1c774be2d1b6d4ca2857eeceb4b4c6ef05ff7f070034780991cc196dc6f5ffe8f49f1f844a00f7ac614638f850d3e2e3a78ee644c8
7
+ data.tar.gz: 5a3154eeb7aa9368802961166c6fa89ab98c485d6c182d9a80d57d51edd0a09170da59237794d482fc5b1f470a19ce90f59ad5e297b864f6f633b028ea28fc3d
data/.rubocop.yml CHANGED
@@ -1,5 +1,5 @@
1
1
  AllCops:
2
- TargetRubyVersion: 2.5
2
+ TargetRubyVersion: 2.2
3
3
  Exclude:
4
4
  - 'bin/*'
5
5
  - 'Guardfile'
@@ -1,39 +1,79 @@
1
1
  module Userlist
2
2
  class Config
3
3
  DEFAULT_CONFIGURATION = {
4
- push_key: nil,
5
- push_endpoint: 'https://push.userlist.io/'
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 = DEFAULT_CONFIGURATION
11
+ @config = default_config
10
12
  .merge(config_from_initialize)
11
13
  .merge(config_from_environment)
12
14
  end
13
15
 
14
- private
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
- DEFAULT_CONFIGURATION.keys.each_with_object({}) do |key, config|
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 respond_to_missing?(name)
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
- config.key?(name.to_sym)
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
- config.public_send(method, name, *args, &block)
76
+ send(method, name, *args, &block)
37
77
  else
38
78
  super
39
79
  end
@@ -0,0 +1,11 @@
1
+ module Userlist
2
+ module Logging
3
+ def self.included(mod)
4
+ mod.send(:extend, self)
5
+ end
6
+
7
+ def logger
8
+ Userlist.logger
9
+ end
10
+ end
11
+ end
@@ -4,10 +4,12 @@ require 'net/http'
4
4
  require 'openssl'
5
5
 
6
6
  module Userlist
7
- module Push
7
+ class Push
8
8
  class Client
9
- def initialize(config = Userlist.config)
10
- @config = config
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(endpoint, payload = {})
35
- request = Net::HTTP::Post.new(endpoint)
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,11 @@
1
+ module Userlist
2
+ class Push
3
+ module Strategies
4
+ class Null
5
+ def initialize(*); end
6
+
7
+ def call(*); end
8
+ end
9
+ end
10
+ end
11
+ 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
- module Push
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
@@ -1,3 +1,3 @@
1
1
  module Userlist
2
- VERSION = '0.1.0'.freeze
2
+ VERSION = '0.2.0'.freeze
3
3
  end
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.1.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-01-18 00:00:00.000000000 Z
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: '0'
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.6.11
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