bps 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 5de5e6e993f0b96c93224163f85fc0cd3ef5a9ecf7d03487df35315ee3bd153b
4
+ data.tar.gz: cfaa3b821549d70bee5094321553123493d9e66fca4cf15fa50ccdb0f4c5a04d
5
+ SHA512:
6
+ metadata.gz: c4d068aa91b285af2a124a258a13207298c3c0f9202fe39f197e21a9fd49a53f433de12cf4817b78b265b2920deeaa402a014cba0fe74da3b258570688b8fb1e
7
+ data.tar.gz: 4b7f0fa9bf89e079e05312e4d202af107b102c51079166704ca4641146942a0b3a692a0605d52bf0e46bbb8d8847b0969a8c8c20e763308d160988cff1c7d9c5
@@ -0,0 +1,19 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = 'bps'
3
+ s.version = File.read(File.expand_path('../../.version', __dir__)).strip
4
+ s.platform = Gem::Platform::RUBY
5
+
6
+ s.licenses = ['Apache-2.0']
7
+ s.summary = 'Multi-platform pubsub adapter'
8
+ s.description = 'Minimalist abstraction for publish-subscribe'
9
+
10
+ s.authors = ['Black Square Media']
11
+ s.email = 'info@blacksquaremedia.com'
12
+ s.homepage = 'https://github.com/bsm/bps'
13
+
14
+ s.executables = []
15
+ s.files = `git ls-files`.split("\n")
16
+ s.test_files = `git ls-files -- spec/*`.split("\n")
17
+ s.require_paths = ['lib']
18
+ s.required_ruby_version = '>= 2.6.0'
19
+ end
@@ -0,0 +1,52 @@
1
+ require 'uri'
2
+ require 'cgi'
3
+
4
+ module BPS
5
+ autoload :Coercer, 'bps/coercer'
6
+
7
+ module Publisher
8
+ autoload :Abstract, 'bps/publisher/abstract'
9
+
10
+ def self.register(*schemes, &resolver)
11
+ @registry ||= {}
12
+ schemes.each do |scheme|
13
+ @registry[scheme] = resolver
14
+ end
15
+ end
16
+
17
+ def self.resolve(url)
18
+ url = url.is_a?(::URI) ? url.dup : URI.parse(url)
19
+ rsl = @registry[url.scheme]
20
+ raise ArgumentError, "Unable to resolve publisher #{url}, scheme #{url.scheme} is not registered" unless rsl
21
+
22
+ opts = {}
23
+ CGI.parse(url.query.to_s).each do |key, values|
24
+ opts[key.to_sym] = values.first
25
+ end
26
+ rsl.call(url, opts)
27
+ end
28
+ end
29
+
30
+ module Subscriber
31
+ autoload :Abstract, 'bps/subscriber/abstract'
32
+
33
+ def self.register(*schemes, &resolver)
34
+ @registry ||= {}
35
+ schemes.each do |scheme|
36
+ @registry[scheme] = resolver
37
+ end
38
+ end
39
+
40
+ def self.resolve(url)
41
+ url = url.is_a?(::URI) ? url.dup : URI.parse(url)
42
+ rsl = @registry[url.scheme]
43
+ raise ArgumentError, "Unable to resolve subscriber #{url}, scheme #{url.scheme} is not registered" unless rsl
44
+
45
+ opts = {}
46
+ CGI.parse(url.query.to_s).each do |key, values|
47
+ opts[key.to_sym] = values.first
48
+ end
49
+ rsl.call(url, opts)
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,64 @@
1
+ require 'set'
2
+
3
+ module BPS
4
+ class Coercer
5
+ TRUE_VALUES = [true, 'TRUE', 'true', 'T', 't', 1, '1'].to_set
6
+
7
+ attr_reader :schema
8
+
9
+ def initialize(schema)
10
+ validate!(schema)
11
+ @schema = schema
12
+ end
13
+
14
+ def coerce(hash)
15
+ coerce_with(@schema, hash)
16
+ end
17
+
18
+ private
19
+
20
+ def validate!(schema)
21
+ schema.each do |key, type|
22
+ case type
23
+ when :string, :symbol, :int, :float, :bool
24
+ # OK
25
+ when Hash
26
+ validate!(type)
27
+ when Array
28
+ raise ArgumentError, "Array types must have exactly one entry, but was (#{key} => #{type.inspect})" unless type.size == 1
29
+ else
30
+ raise ArgumentError, "Unknown type #{type.inspect}"
31
+ end
32
+ end
33
+ end
34
+
35
+ def coerce_with(schema, hash)
36
+ clone = {}
37
+ schema.each do |key, type|
38
+ next unless hash.key?(key)
39
+
40
+ clone[key] = coerce_value(type, hash[key])
41
+ end
42
+ clone
43
+ end
44
+
45
+ def coerce_value(type, val)
46
+ case type
47
+ when :string
48
+ val&.to_s
49
+ when :symbol
50
+ val&.to_sym
51
+ when :int
52
+ val&.to_i
53
+ when :float
54
+ val&.to_f
55
+ when :bool
56
+ val.nil? ? nil : TRUE_VALUES.include?(val)
57
+ when Hash
58
+ val.is_a?(Hash) ? coerce_with(type, val) : nil
59
+ when Array
60
+ Array(val).map {|v| coerce_value(type[0], v) }
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,24 @@
1
+ module BPS
2
+ module Publisher
3
+ class Abstract
4
+ class Topic
5
+ # Publish a message.
6
+ def publish(_message, **_opts)
7
+ raise 'not implemented'
8
+ end
9
+
10
+ # Flush any remaining buffer.
11
+ def flush(**); end
12
+ end
13
+
14
+ # Retrieve a topic handle.
15
+ # @params [String] name the topic name.
16
+ def topic(_name)
17
+ raise 'not implemented'
18
+ end
19
+
20
+ # Close the publisher.
21
+ def close; end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,14 @@
1
+ module BPS
2
+ module Subscriber
3
+ class Abstract
4
+ # Subscribe to a topic
5
+ # @params [String] topic the topic name.
6
+ def subscribe(_topic, **)
7
+ raise 'not implemented'
8
+ end
9
+
10
+ # Close the subscriber.
11
+ def close; end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,42 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe BPS::Coercer do
4
+ subject do
5
+ described_class.new(
6
+ name: :string,
7
+ codec: :symbol,
8
+ retries: :int,
9
+ backoff: :float,
10
+ idempotent: :bool,
11
+ tags: [:string],
12
+ )
13
+ end
14
+
15
+ let :options do
16
+ {
17
+ name: 123,
18
+ codec: 'snappy',
19
+ retries: '4',
20
+ backoff: '10',
21
+ idempotent: '1',
22
+ tags: [:foo, 33],
23
+ extra: 'foo',
24
+ }
25
+ end
26
+
27
+ it 'should validate' do
28
+ expect { described_class.new(name: :unknown) }.to raise_error(ArgumentError, /Unknown type :unknown/)
29
+ expect { described_class.new(bad: []) }.to raise_error(ArgumentError, /Array types must have exactly one entry/)
30
+ end
31
+
32
+ it 'should coerce options' do
33
+ expect(subject.coerce(options)).to eq(
34
+ name: '123',
35
+ codec: :snappy,
36
+ retries: 4,
37
+ backoff: 10.0,
38
+ idempotent: true,
39
+ tags: %w[foo 33],
40
+ )
41
+ end
42
+ end
metadata ADDED
@@ -0,0 +1,49 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: bps
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Black Square Media
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2020-07-01 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: Minimalist abstraction for publish-subscribe
14
+ email: info@blacksquaremedia.com
15
+ executables: []
16
+ extensions: []
17
+ extra_rdoc_files: []
18
+ files:
19
+ - bps.gemspec
20
+ - lib/bps.rb
21
+ - lib/bps/coercer.rb
22
+ - lib/bps/publisher/abstract.rb
23
+ - lib/bps/subscriber/abstract.rb
24
+ - spec/bps/coercer_spec.rb
25
+ homepage: https://github.com/bsm/bps
26
+ licenses:
27
+ - Apache-2.0
28
+ metadata: {}
29
+ post_install_message:
30
+ rdoc_options: []
31
+ require_paths:
32
+ - lib
33
+ required_ruby_version: !ruby/object:Gem::Requirement
34
+ requirements:
35
+ - - ">="
36
+ - !ruby/object:Gem::Version
37
+ version: 2.6.0
38
+ required_rubygems_version: !ruby/object:Gem::Requirement
39
+ requirements:
40
+ - - ">="
41
+ - !ruby/object:Gem::Version
42
+ version: '0'
43
+ requirements: []
44
+ rubygems_version: 3.1.4
45
+ signing_key:
46
+ specification_version: 4
47
+ summary: Multi-platform pubsub adapter
48
+ test_files:
49
+ - spec/bps/coercer_spec.rb