polyn 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,136 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright 2021-2022 Spiff, Inc.
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining a copy of this
6
+ # software and associated documentation files (the "Software"), to deal in the Software
7
+ # without restriction, including without limitation the rights to use, copy, modify, merge,
8
+ # publish, distribute, sublicense, and/or sell copies of the Software, and to permit
9
+ # persons to whom the Software is furnished to do so, subject to the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be included in all copies or
12
+ # substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
15
+ # NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
16
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
17
+ # DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
19
+
20
+ module Polyn
21
+ module Serializers
22
+ ##
23
+ # Handles serializing and deserializing data to and from JSON.
24
+ class Json
25
+ def self.serialize!(nats, event, **opts)
26
+ validate_event_instance!(event)
27
+ event = event.to_h
28
+ validate!(nats, event, **opts)
29
+ JSON.generate(event)
30
+ end
31
+
32
+ def self.deserialize!(nats, json, **opts)
33
+ data = deserialize(nats, json, **opts)
34
+ raise data if data.is_a?(Polyn::Errors::Error)
35
+
36
+ data
37
+ end
38
+
39
+ def self.deserialize(nats, json, **opts)
40
+ data = decode(json)
41
+ return data if data.is_a?(Polyn::Errors::Error)
42
+
43
+ error = validate(nats, data, **opts)
44
+ return error if error.is_a?(Polyn::Errors::Error)
45
+
46
+ data = Polyn::Utils::Hash.deep_symbolize_keys(data)
47
+ Event.new(data)
48
+ end
49
+
50
+ def self.decode(json)
51
+ JSON.parse(json)
52
+ rescue JSON::ParserError
53
+ Polyn::Errors::ValidationError.new("Polyn was unable to decode the following message: \n#{json}")
54
+ end
55
+
56
+ def self.validate!(nats, event, **opts)
57
+ result = validate(nats, event, **opts)
58
+ raise result if result.is_a?(Polyn::Errors::Error)
59
+ end
60
+
61
+ def self.validate(nats, event, **opts)
62
+ error = validate_cloud_event(event)
63
+ return error if error.is_a?(Polyn::Errors::Error)
64
+
65
+ validate_data(nats, event, **opts)
66
+ end
67
+
68
+ def self.validate_event_instance!(event)
69
+ if event.instance_of?(Polyn::Event)
70
+ event
71
+ else
72
+ raise Polyn::Errors::ValidationError,
73
+ "Can only serialize `Polyn::Event` instances. got #{event}"
74
+ end
75
+ end
76
+
77
+ def self.validate_cloud_event(event)
78
+ cloud_event_schema = Polyn::CloudEvent.to_h
79
+ validate_schema(cloud_event_schema, event)
80
+ end
81
+
82
+ def self.validate_data(nats, event, **opts)
83
+ type = get_event_type(event)
84
+ return type if type.is_a?(Polyn::Errors::Error)
85
+
86
+ schema = get_schema(nats, type, **opts)
87
+ return schema if schema.is_a?(Polyn::Errors::Error)
88
+
89
+ validate_schema(schema, event)
90
+ end
91
+
92
+ def self.validate_schema(schema, event)
93
+ schema = JSONSchemer.schema(schema)
94
+ errors = schema.validate(event).to_a
95
+ errors = format_schema_errors(errors)
96
+ unless errors.empty?
97
+ return Polyn::Errors::ValidationError.new(combined_error_message(event,
98
+ errors))
99
+ end
100
+
101
+ errors
102
+ end
103
+
104
+ def self.get_event_type(event)
105
+ if event["type"]
106
+ Polyn::Naming.trim_domain_prefix(event["type"])
107
+ else
108
+ Polyn::Errors::ValidationError.new(
109
+ "Could not find a `type` in message #{event.inspect} \nEvery event must have a `type`",
110
+ )
111
+ end
112
+ end
113
+
114
+ def self.get_schema(nats, type, **opts)
115
+ Polyn::SchemaStore.get(nats, type, name: store_name(**opts))
116
+ end
117
+
118
+ def self.format_schema_errors(errors)
119
+ errors.map do |error|
120
+ "Property: `#{error['data_pointer']}` - #{error['type']} - #{error['details']}"
121
+ end
122
+ end
123
+
124
+ def self.combined_error_message(event, errors)
125
+ [
126
+ "Polyn event #{event['id']} from #{event['source']} is not valid",
127
+ "Event data: #{event.inspect}",
128
+ ].concat(errors).join("\n")
129
+ end
130
+
131
+ def self.store_name(**opts)
132
+ opts[:store_name] || Polyn::SchemaStore.store_name
133
+ end
134
+ end
135
+ end
136
+ end
@@ -0,0 +1,82 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright 2021-2022 Spiff, Inc.
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining a copy of this
6
+ # software and associated documentation files (the "Software"), to deal in the Software
7
+ # without restriction, including without limitation the rights to use, copy, modify, merge,
8
+ # publish, distribute, sublicense, and/or sell copies of the Software, and to permit
9
+ # persons to whom the Software is furnished to do so, subject to the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be included in all copies or
12
+ # substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
15
+ # NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
16
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
17
+ # DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
19
+
20
+ module Polyn
21
+ module Utils
22
+ ##
23
+ # Utilities for hash manipulation.
24
+ module Hash
25
+ ##
26
+ # Deep symbolize keys of a hash.
27
+ #
28
+ # @param hash [::Hash] The hash to symbolize.
29
+ #
30
+ # @return [::Hash] The symbolized hash.
31
+ def self.deep_symbolize_keys(hash)
32
+ return hash.map { |item| deep_symbolize_keys(item) } if hash.is_a?(::Array)
33
+ return hash unless hash.is_a?(::Hash)
34
+
35
+ hash.each_with_object({}) do |(key, value), result|
36
+ result[key.to_sym] = deep_symbolize_keys(value)
37
+ end
38
+ end
39
+
40
+ ##
41
+ # Deep stringifies keys
42
+ #
43
+ # @param hash [::Hash] The hash to stringify.
44
+ #
45
+ # @return [::Hash] The stringified hash.
46
+ def self.deep_stringify_keys(hash)
47
+ return hash.map { |item| deep_stringify_keys(item) } if hash.is_a?(::Array)
48
+ return hash unless hash.is_a?(::Hash)
49
+
50
+ hash.each_with_object({}) do |(key, value), result|
51
+ result[key.to_s] = deep_stringify_keys(value)
52
+ end
53
+ end
54
+
55
+ ##
56
+ # Deep camelize keys
57
+ #
58
+ # @param hash [::Hash] The hash to camelize.
59
+ #
60
+ # @return [::Hash] The camelized hash.
61
+ def self.deep_camelize_keys(hash)
62
+ hash.each_with_object({}) do |(key, value), result|
63
+ result[String.to_camel_case(key)] =
64
+ value.is_a?(::Hash) ? deep_camelize_keys(value) : value
65
+ end
66
+ end
67
+
68
+ ##
69
+ # Deep snake cases keys
70
+ #
71
+ # @param hash [::Hash] The hash to snake case.
72
+ #
73
+ # @return [::Hash] The snake cased hash.
74
+ def self.deep_snake_case_keys(hash)
75
+ hash.each_with_object({}) do |(key, value), result|
76
+ result[String.to_snake_case(key.to_s)] =
77
+ value.is_a?(::Hash) ? deep_snake_case_keys(value) : value
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright 2021-2022 Spiff, Inc.
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining a copy of this
6
+ # software and associated documentation files (the "Software"), to deal in the Software
7
+ # without restriction, including without limitation the rights to use, copy, modify, merge,
8
+ # publish, distribute, sublicense, and/or sell copies of the Software, and to permit
9
+ # persons to whom the Software is furnished to do so, subject to the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be included in all copies or
12
+ # substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
15
+ # NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
16
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
17
+ # DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
19
+
20
+ module Polyn
21
+ module Utils
22
+ ##
23
+ # Utilities for working with strings.
24
+ module String
25
+ ##
26
+ # Converts a string to camel case.
27
+ #
28
+ # @param str ing [String] The string to convert.
29
+ #
30
+ # @return [String] The camel cased string.
31
+ def self.to_camel_case(str)
32
+ str = str.to_s.split("_").map(&:capitalize).join
33
+ str[0] = str[0].downcase
34
+ str
35
+ end
36
+
37
+ def self.to_snake_case(str)
38
+ str.gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
39
+ .gsub(/([a-z\d])([A-Z])/, '\1_\2')
40
+ .tr("-", "_")
41
+ .downcase
42
+ end
43
+
44
+ def self.to_class_name(str)
45
+ str.split("_").map(&:capitalize).join
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright 2021-2022 Spiff, Inc.
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining a copy of this
6
+ # software and associated documentation files (the "Software"), to deal in the Software
7
+ # without restriction, including without limitation the rights to use, copy, modify, merge,
8
+ # publish, distribute, sublicense, and/or sell copies of the Software, and to permit
9
+ # persons to whom the Software is furnished to do so, subject to the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be included in all copies or
12
+ # substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
15
+ # NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
16
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
17
+ # DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
19
+
20
+ require "polyn/utils/hash"
21
+ require "polyn/utils/string"
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Polyn
4
+ VERSION = "0.1.0"
5
+ end
data/lib/polyn.rb ADDED
@@ -0,0 +1,116 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright 2021-2022 Spiff, Inc.
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining a copy of this
6
+ # software and associated documentation files (the "Software"), to deal in the Software
7
+ # without restriction, including without limitation the rights to use, copy, modify, merge,
8
+ # publish, distribute, sublicense, and/or sell copies of the Software, and to permit
9
+ # persons to whom the Software is furnished to do so, subject to the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be included in all copies or
12
+ # substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
15
+ # NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
16
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
17
+ # DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
19
+
20
+ # Loading all our classes up front
21
+ require "json_schemer"
22
+ require "json"
23
+ require "nats/client"
24
+ require "securerandom"
25
+
26
+ require "polyn/configuration"
27
+ require "polyn/cloud_event"
28
+ require "polyn/errors/errors"
29
+ require "polyn/event"
30
+ require "polyn/naming"
31
+ require "polyn/pull_subscriber"
32
+ require "polyn/schema_store"
33
+ require "polyn/serializers/json"
34
+ require "polyn/utils/utils"
35
+ require "polyn/version"
36
+
37
+ ##
38
+ # Polyn is a Reactive service framework.
39
+ module Polyn
40
+ ##
41
+ # Publishes a message on the Polyn network.
42
+ #
43
+ # @param nats [Object] Connected NATS instance from `NATS.connect`
44
+ # @param type [String] The type of event
45
+ # @param data [any] The data to include in the event
46
+ # @option options [String] :source - information to specify the source of the event
47
+ # @option options [String] :triggered_by - The event that triggered this one.
48
+ # Will use information from the event to build up the `polyntrace` data
49
+ # @option options [String] :reply_to - Reply to a specific topic
50
+ # @option options [String] :header - Headers to include in the message
51
+ def self.publish(nats, type, data, **opts)
52
+ event = Event.new({
53
+ type: type,
54
+ source: opts[:source],
55
+ data: data,
56
+ triggered_by: opts[:triggered_by],
57
+ })
58
+
59
+ json = Polyn::Serializers::Json.serialize!(nats, event, **opts)
60
+
61
+ nats.publish(type, json, opts[:reply_to], header: opts[:header])
62
+ end
63
+
64
+ ## Create subscription which is dispatched asynchronously
65
+ # and sends messages to a callback.
66
+ #
67
+ # @param nats [Object] Connected NATS instance from `NATS.connect`
68
+ # @param type [String] The type of event
69
+ # @option options [String] :queue - Queue group to add subscriber to
70
+ # @option options [String] :max - Max msgs before unsubscribing
71
+ # @option options [String] :pending_msgs_limit
72
+ # @option options [String] :pending_bytes_limit
73
+ def self.subscribe(nats, type, opts = {}, &callback)
74
+ nats.subscribe(type, opts) do |msg|
75
+ event = Polyn::Serializers::Json.deserialize!(nats, msg.data,
76
+ store_name: opts[:store_name])
77
+ msg.data = event
78
+ callback.call(msg)
79
+ end
80
+ end
81
+
82
+ ##
83
+ # Subscribe to a pull consumer that already exists in the NATS server
84
+ #
85
+ # @param nats [Object] Connected NATS instance from `NATS.connect`
86
+ # @param type [String] The type of event
87
+ # @option options [String] :source - If the `source` portion of the consumer name
88
+ # is more than the `source_root`
89
+ def self.pull_subscribe(nats, type, **opts)
90
+ Polyn::PullSubscriber.new({ nats: nats, type: type, source: opts[:source] })
91
+ end
92
+
93
+ # nats-pure will create a consumer if the one you passed does not exist.
94
+ # Polyn wants to avoid this functionality and instead encourage
95
+ # consumer creation in the centralized `events` codebase so that
96
+ # it's documented, discoverable, and polyn-cli can manage it
97
+ def self.validate_consumer_exists!(nats, stream, consumer_name)
98
+ nats.jetstream.consumer_info(stream, consumer_name)
99
+ rescue NATS::JetStream::Error::NotFound
100
+ raise Polyn::Errors::ValidationError,
101
+ "Consumer #{consumer_name} does not exist. Use polyn-cli to create "\
102
+ "it before attempting to subscribe"
103
+ end
104
+
105
+ ##
106
+ # Configuration information for Polyn
107
+ def self.configuration
108
+ @configuration ||= Configuration.new
109
+ end
110
+
111
+ ##
112
+ # Configuration block to configure Polyn
113
+ def self.configure
114
+ yield(configuration)
115
+ end
116
+ end
data/polyn.gemspec ADDED
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright 2021-2022 Spiff, Inc.
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining a copy of this
6
+ # software and associated documentation files (the "Software"), to deal in the Software
7
+ # without restriction, including without limitation the rights to use, copy, modify, merge,
8
+ # publish, distribute, sublicense, and/or sell copies of the Software, and to permit
9
+ # persons to whom the Software is furnished to do so, subject to the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be included in all copies or
12
+ # substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
15
+ # NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
16
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
17
+ # DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
19
+
20
+ require_relative "lib/polyn/version"
21
+
22
+ Gem::Specification.new do |spec|
23
+ spec.name = "polyn"
24
+ spec.version = Polyn::VERSION
25
+ spec.authors = ["Jarod", "Brandyn Bennett"]
26
+ spec.email = ["jarod.reid@spiff.com", "brandyn.bennett@spiff.com"]
27
+
28
+ spec.summary = "Polyn Service Framework"
29
+ spec.description = "A lightweight reactive microservice framework."
30
+ spec.homepage = "https://github.com/spiffinc/polyn-ruby"
31
+ spec.license = "MIT"
32
+ spec.required_ruby_version = Gem::Requirement.new(">= 2.4.0")
33
+
34
+ # spec.metadata["allowed_push_host"] = "TODO: Set to 'http://mygemserver.com'"
35
+
36
+ spec.metadata["homepage_uri"] = spec.homepage
37
+ # spec.metadata["source_code_uri"] = "TODO: Put your gem's public repo URL here."
38
+ # spec.metadata["changelog_uri"] = "TODO: Put your gem's CHANGELOG.md URL here."
39
+
40
+ # Specify which files should be added to the gem when it is released.
41
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
42
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
43
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{\A(?:test|spec|features)/}) }
44
+ end
45
+ spec.bindir = "exe"
46
+ spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
47
+ spec.require_paths = ["lib"]
48
+
49
+ spec.add_dependency "json_schemer", "~> 0.2"
50
+ spec.add_dependency "nats-pure", "~> 2.0"
51
+ spec.add_dependency "yard", "~> 0.9"
52
+ end
metadata ADDED
@@ -0,0 +1,123 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: polyn
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Jarod
8
+ - Brandyn Bennett
9
+ autorequire:
10
+ bindir: exe
11
+ cert_chain: []
12
+ date: 2022-08-18 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: json_schemer
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - "~>"
19
+ - !ruby/object:Gem::Version
20
+ version: '0.2'
21
+ type: :runtime
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - "~>"
26
+ - !ruby/object:Gem::Version
27
+ version: '0.2'
28
+ - !ruby/object:Gem::Dependency
29
+ name: nats-pure
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - "~>"
33
+ - !ruby/object:Gem::Version
34
+ version: '2.0'
35
+ type: :runtime
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - "~>"
40
+ - !ruby/object:Gem::Version
41
+ version: '2.0'
42
+ - !ruby/object:Gem::Dependency
43
+ name: yard
44
+ requirement: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - "~>"
47
+ - !ruby/object:Gem::Version
48
+ version: '0.9'
49
+ type: :runtime
50
+ prerelease: false
51
+ version_requirements: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - "~>"
54
+ - !ruby/object:Gem::Version
55
+ version: '0.9'
56
+ description: A lightweight reactive microservice framework.
57
+ email:
58
+ - jarod.reid@spiff.com
59
+ - brandyn.bennett@spiff.com
60
+ executables: []
61
+ extensions: []
62
+ extra_rdoc_files: []
63
+ files:
64
+ - ".github/workflows/ruby.yml"
65
+ - ".gitignore"
66
+ - ".rspec"
67
+ - ".rubocop.yml"
68
+ - ".tool-versions"
69
+ - ".travis.yml"
70
+ - CHANGELOG.md
71
+ - CODE_OF_CONDUCT.md
72
+ - Gemfile
73
+ - Gemfile.lock
74
+ - LICENSE.txt
75
+ - README.md
76
+ - Rakefile
77
+ - bin/console
78
+ - bin/setup
79
+ - docker-compose.yml
80
+ - lib/cloud-event-schema.json
81
+ - lib/polyn.rb
82
+ - lib/polyn/cloud_event.rb
83
+ - lib/polyn/configuration.rb
84
+ - lib/polyn/errors/configuration_error.rb
85
+ - lib/polyn/errors/error.rb
86
+ - lib/polyn/errors/errors.rb
87
+ - lib/polyn/errors/schema_error.rb
88
+ - lib/polyn/errors/validation_error.rb
89
+ - lib/polyn/event.rb
90
+ - lib/polyn/naming.rb
91
+ - lib/polyn/pull_subscriber.rb
92
+ - lib/polyn/schema_store.rb
93
+ - lib/polyn/serializers/json.rb
94
+ - lib/polyn/utils/hash.rb
95
+ - lib/polyn/utils/string.rb
96
+ - lib/polyn/utils/utils.rb
97
+ - lib/polyn/version.rb
98
+ - polyn.gemspec
99
+ homepage: https://github.com/spiffinc/polyn-ruby
100
+ licenses:
101
+ - MIT
102
+ metadata:
103
+ homepage_uri: https://github.com/spiffinc/polyn-ruby
104
+ post_install_message:
105
+ rdoc_options: []
106
+ require_paths:
107
+ - lib
108
+ required_ruby_version: !ruby/object:Gem::Requirement
109
+ requirements:
110
+ - - ">="
111
+ - !ruby/object:Gem::Version
112
+ version: 2.4.0
113
+ required_rubygems_version: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ requirements: []
119
+ rubygems_version: 3.2.33
120
+ signing_key:
121
+ specification_version: 4
122
+ summary: Polyn Service Framework
123
+ test_files: []