maitredee 0.8.2 → 0.8.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.circleci/config.yml +13 -1
- data/.yardopts +1 -0
- data/Gemfile.lock +10 -4
- data/README.md +171 -14
- data/lib/maitredee.rb +66 -2
- data/lib/maitredee/active_job.rb +10 -0
- data/lib/maitredee/adapters/base_adapter.rb +18 -0
- data/lib/maitredee/adapters/sns_sqs_adapter.rb +20 -1
- data/lib/maitredee/adapters/test_adapter.rb +5 -1
- data/lib/maitredee/publisher.rb +33 -0
- data/lib/maitredee/subscriber.rb +64 -7
- data/lib/maitredee/version.rb +1 -1
- data/maitredee.gemspec +1 -0
- metadata +19 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 52dd1ef33db5eadd01dc66f0c26f37a7ac8b5d19bfe865ec0cae03535900fedb
|
4
|
+
data.tar.gz: d0b9a43bb7eab1208fd2ff3ba5668a65d2fb870d755cbd61fb41d84274e53521
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 24dd10b8944d88da64c2386e3660aa1cb1263523aa7e8ed0fa5ccfd430af698b787c49de230b8a8e9f402330a74e5de284bf5b9abb2fb5773e04a87f4136283d
|
7
|
+
data.tar.gz: 6e4ee09a3a305a2640bfb354031ae0000a121dba80c3bc303e7f8d68898ad234231757a3d7e1d6eb87afba2417e377e4547f66c5bc13de933f38aa1c76c4c15c
|
data/.circleci/config.yml
CHANGED
@@ -51,11 +51,23 @@ jobs:
|
|
51
51
|
- run:
|
52
52
|
name: run rspec tests
|
53
53
|
command: |
|
54
|
-
|
54
|
+
mkdir /tmp/test-results
|
55
|
+
bundle exec rspec \
|
56
|
+
--profile 10 \
|
57
|
+
--format progress
|
55
58
|
- run:
|
56
59
|
name: run sns/sqs tests
|
57
60
|
command: |
|
58
61
|
INTEGRATION_TEST=sns_sqs bundle exec rspec --format progress
|
62
|
+
- run:
|
63
|
+
name: run doctests
|
64
|
+
command: bundle exec goodread-rb README.md
|
65
|
+
# Collect reports
|
66
|
+
- store_test_results:
|
67
|
+
path: /tmp/test-results
|
68
|
+
- store_artifacts:
|
69
|
+
path: /tmp/test-results
|
70
|
+
destination: test-results
|
59
71
|
workflows:
|
60
72
|
version: 2
|
61
73
|
build_and_test:
|
data/.yardopts
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--markup=markdown
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
maitredee (0.8.
|
4
|
+
maitredee (0.8.3)
|
5
5
|
activesupport
|
6
6
|
aws-sdk-sns
|
7
7
|
aws-sdk-sqs
|
@@ -34,8 +34,8 @@ GEM
|
|
34
34
|
minitest (~> 5.1)
|
35
35
|
tzinfo (~> 1.1)
|
36
36
|
aws-eventstream (1.0.1)
|
37
|
-
aws-partitions (1.
|
38
|
-
aws-sdk-core (3.44.
|
37
|
+
aws-partitions (1.127.0)
|
38
|
+
aws-sdk-core (3.44.1)
|
39
39
|
aws-eventstream (~> 1.0)
|
40
40
|
aws-partitions (~> 1.0)
|
41
41
|
aws-sigv4 (~> 1.0)
|
@@ -50,6 +50,7 @@ GEM
|
|
50
50
|
builder (3.2.3)
|
51
51
|
byebug (10.0.2)
|
52
52
|
coderay (1.1.2)
|
53
|
+
colorize (0.8.1)
|
53
54
|
concurrent-ruby (1.1.3)
|
54
55
|
crass (1.0.4)
|
55
56
|
diff-lcs (1.3)
|
@@ -57,8 +58,12 @@ GEM
|
|
57
58
|
ecma-re-validator (0.2.0)
|
58
59
|
regexp_parser (~> 1.2)
|
59
60
|
erubi (1.7.1)
|
61
|
+
gemoji (3.0.0)
|
60
62
|
globalid (0.4.1)
|
61
63
|
activesupport (>= 4.2.0)
|
64
|
+
goodread (0.3.2)
|
65
|
+
colorize (~> 0.8)
|
66
|
+
gemoji (~> 3.0)
|
62
67
|
hana (1.3.4)
|
63
68
|
i18n (1.1.1)
|
64
69
|
concurrent-ruby (~> 1.0)
|
@@ -128,6 +133,7 @@ DEPENDENCIES
|
|
128
133
|
activejob
|
129
134
|
bundler (~> 1.17)
|
130
135
|
dotenv
|
136
|
+
goodread
|
131
137
|
maitredee!
|
132
138
|
pry
|
133
139
|
pry-byebug
|
@@ -137,4 +143,4 @@ DEPENDENCIES
|
|
137
143
|
rspec-mocks (~> 3.0)
|
138
144
|
|
139
145
|
BUNDLED WITH
|
140
|
-
1.17.
|
146
|
+
1.17.2
|
data/README.md
CHANGED
@@ -1,8 +1,26 @@
|
|
1
1
|
# Maitredee
|
2
2
|
|
3
|
-
|
3
|
+
An opinionated pub/sub framework.
|
4
4
|
|
5
|
-
|
5
|
+
## Table of Contents
|
6
|
+
- [Overview](#overview)
|
7
|
+
- [Installation](#installation)
|
8
|
+
- [Configuration](#configuration)
|
9
|
+
- [Publisher](#publisher)
|
10
|
+
- [Subscriber](#subscriber)
|
11
|
+
- [Validation schema](#validation-schema)
|
12
|
+
- [Misc](#misc)
|
13
|
+
- [Development](#development)
|
14
|
+
- [Contributing](#contributing)
|
15
|
+
- [License](#license)
|
16
|
+
- [Code of Conduct](#code-of-conduct)
|
17
|
+
|
18
|
+
## Overview
|
19
|
+
We made maitredee to simplify publishing and subscribing to events for our junior developers. We tried using kafka but ordered eventing was too complicated.
|
20
|
+
|
21
|
+
We tried to have zero setup required to get this up and running and make it work as simply as sidekiq.
|
22
|
+
|
23
|
+
We hope in the future to add more adapters beyond sns/sqs.
|
6
24
|
|
7
25
|
## Installation
|
8
26
|
|
@@ -20,30 +38,169 @@ Or install it yourself as:
|
|
20
38
|
|
21
39
|
$ gem install maitredee
|
22
40
|
|
23
|
-
##
|
41
|
+
## Configuration
|
42
|
+
|
43
|
+
Required Configuration
|
44
|
+
```ruby
|
45
|
+
Maitredee.namespace = "plated-production"
|
46
|
+
Maitredee.schema_path = Rails.root.join("your/path").to_s
|
47
|
+
Maitredee.client = :sns_sqs
|
48
|
+
```
|
49
|
+
|
50
|
+
These namespace can also be set with the environment variable `MAITREDEE_NAMESPACE`
|
51
|
+
|
52
|
+
### Available clients
|
53
|
+
|
54
|
+
Maitredee currently supports the following clients:
|
55
|
+
|
56
|
+
#### SNS/SQS :sns_sqs
|
57
|
+
You can set the AWS parameters in a variety of ways.
|
58
|
+
Either environment variables or explicitly.
|
59
|
+
Supported environment variables are `MAITREDEE_AWS_ACCESS_KEY_ID`, `MAITREDEE_AWS_SECRET_ACCESS_KEY`, `MAITREDEE_AWS_REGION` and then falls back to default AWS keys.
|
60
|
+
|
61
|
+
if you wish to set it explicitly:
|
62
|
+
```ruby
|
63
|
+
Maitredee.set_client(
|
64
|
+
:sns_sqs,
|
65
|
+
access_key_id: "",
|
66
|
+
secret_access_key: "",
|
67
|
+
region: ""
|
68
|
+
)
|
69
|
+
```
|
70
|
+
|
71
|
+
#### Test :test
|
72
|
+
|
73
|
+
This is used for testing.
|
74
|
+
|
75
|
+
```ruby
|
76
|
+
Maitredee.client = :test
|
77
|
+
```
|
78
|
+
|
79
|
+
When you pubish anything through Maitredee it will be logged in the test client for test verification.
|
80
|
+
|
81
|
+
You should reset the client at the beginning of every test with `Maitredee.client.reset`
|
82
|
+
|
83
|
+
## Publisher
|
84
|
+
|
85
|
+
Create a publisher class for your topic and inherit from `Maitredee::Publisher`
|
86
|
+
Optionally define the default topic, event_name, or validation schema with `publish_defaults`
|
87
|
+
Maitredee will call `process` on your publisher when it is called. Define a method `process` that calls `publish` with the parameters of your choosing. `Publish` will default the `topic`, `event_name`, and `schema_name` from your publish_defaults if not given.
|
88
|
+
|
89
|
+
```ruby goodread
|
90
|
+
require "maitredee"
|
91
|
+
|
92
|
+
class MyPublisher < Maitredee::Publisher
|
93
|
+
publish_defaults(
|
94
|
+
topic_name: :your_default_topic,
|
95
|
+
event_name: :your_default_event_name,
|
96
|
+
schema_name: :your_default_schema
|
97
|
+
)
|
98
|
+
|
99
|
+
attr_reader :model
|
100
|
+
|
101
|
+
def initialize(model)
|
102
|
+
@model = model
|
103
|
+
end
|
104
|
+
|
105
|
+
def process
|
106
|
+
publish(
|
107
|
+
topic_name: :my_topic,
|
108
|
+
event_name: :event_name_is_optional,
|
109
|
+
schema_name: :schema_name,
|
110
|
+
primary_key: "optionalKey",
|
111
|
+
body: {
|
112
|
+
id: model.id,
|
113
|
+
name: model.name
|
114
|
+
}
|
115
|
+
)
|
116
|
+
end
|
117
|
+
end
|
118
|
+
```
|
119
|
+
|
120
|
+
|
121
|
+
### Publishing a message
|
122
|
+
To publish a message, simply call `call` on your publisher:
|
123
|
+
```ruby
|
124
|
+
MyPublisher.call(model)
|
125
|
+
```
|
126
|
+
|
127
|
+
Publish will first validate your schema before publishing the message.
|
128
|
+
|
129
|
+
If you have ActiveJob you can also `#call_later` and it will be called asyncronously
|
130
|
+
|
131
|
+
## Subscriber
|
132
|
+
|
133
|
+
```ruby
|
134
|
+
class RecipeSubscriber < Maitredee::Subscriber
|
135
|
+
# this is the topic name
|
136
|
+
subscribe_to :recipes do
|
24
137
|
|
25
|
-
|
138
|
+
# this is the event name optionally say which method to use to process
|
139
|
+
event(:create, to: create)
|
26
140
|
|
27
|
-
|
141
|
+
# event_name will be used as the method name if it is a valid method name, otherwise to: must be set
|
142
|
+
event(:delete)
|
143
|
+
|
144
|
+
# for empty event name just use nil
|
145
|
+
event(nil, to: :process)
|
146
|
+
|
147
|
+
# you can specify a catch all route
|
148
|
+
default_event to: :process
|
149
|
+
end
|
150
|
+
|
151
|
+
def create
|
152
|
+
Recipe.create!(message.body)
|
153
|
+
end
|
154
|
+
|
155
|
+
def process
|
156
|
+
Recipe.find(message.body[:id]).update(message.body)
|
157
|
+
end
|
158
|
+
|
159
|
+
def delete
|
160
|
+
Recipe.find(message.body[:id]).destroy
|
161
|
+
end
|
162
|
+
end
|
163
|
+
```
|
164
|
+
|
165
|
+
## Validating Schemas
|
166
|
+
Maitredee validates your message body schemas using JSON schema ([JSON-schemer] (https://github.com/davishmcclurg/json_schemer)) for both publishing and consuming messages. [Configure] (#configuration) the location of your schemas and provide a JSON file for each of your schemas.
|
167
|
+
|
168
|
+
Example `recipe_v1.json`:
|
169
|
+
```json
|
170
|
+
{
|
171
|
+
"type": "object",
|
172
|
+
"required": ["id", "name", "servings"],
|
173
|
+
"properties": {
|
174
|
+
"id": {
|
175
|
+
"type": "string"
|
176
|
+
},
|
177
|
+
"name": {
|
178
|
+
"type": "string"
|
179
|
+
},
|
180
|
+
"servings": {
|
181
|
+
"type": "number"
|
182
|
+
}
|
183
|
+
}
|
184
|
+
}
|
185
|
+
|
186
|
+
```
|
28
187
|
|
29
|
-
|
30
|
-
* subscriber schema validation
|
31
|
-
* maitredee/test helper methods for subscribers
|
188
|
+
## Misc
|
32
189
|
|
33
|
-
|
190
|
+
### Development
|
34
191
|
|
35
192
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
36
193
|
|
37
194
|
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
38
195
|
|
39
|
-
|
196
|
+
### Contributing
|
40
197
|
|
41
|
-
Bug reports and pull requests are welcome on GitHub at https://github.com/
|
198
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/plated/maitredee. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
|
42
199
|
|
43
|
-
|
200
|
+
### License
|
44
201
|
|
45
202
|
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
46
203
|
|
47
|
-
|
204
|
+
### Code of Conduct
|
48
205
|
|
49
|
-
Everyone interacting in the Maitredee project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/
|
206
|
+
Everyone interacting in the Maitredee project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/plated/maitredee/blob/master/CODE_OF_CONDUCT.md).
|
data/lib/maitredee.rb
CHANGED
@@ -9,15 +9,34 @@ require "pathname"
|
|
9
9
|
require "maitredee/publisher"
|
10
10
|
require "maitredee/subscriber"
|
11
11
|
require "maitredee/version"
|
12
|
+
require "maitredee/adapters/base_adapter"
|
12
13
|
require "maitredee/adapters/sns_sqs_adapter"
|
13
14
|
require "maitredee/railtie" if defined? ::Rails::Railtie
|
14
15
|
|
15
16
|
module Maitredee
|
16
17
|
class << self
|
17
|
-
|
18
|
+
|
19
|
+
# allows you to add a suffix to all your resource names, mostly used for testing but could be useful in other occassions.
|
20
|
+
# @return [String] string appended to all resource names
|
21
|
+
attr_accessor :resource_name_suffix
|
22
|
+
|
23
|
+
# this is the path of the folder in which validation_schema will try to do a lookup. This folder should contain json schemas.
|
24
|
+
# @return [String] path to folder
|
25
|
+
attr_accessor :schema_path
|
26
|
+
|
27
|
+
# the client we use for publishing and setting up workers
|
28
|
+
# @return [Maitredee::Adapters::AbstractAdapter]
|
18
29
|
attr_reader :client
|
19
|
-
attr_writer :app_name, :namespace
|
20
30
|
|
31
|
+
# publishes messages using configured adapter
|
32
|
+
#
|
33
|
+
# @param topic [String] topic name
|
34
|
+
# @param body [Hash, Array, String] Any valid json data that can be validated by json-schema
|
35
|
+
# @param schema_name [String] A valid schema name for publishing data
|
36
|
+
# @param event_name [String, nil] Event name for subscriber routing
|
37
|
+
# @param primary_key [#to_s, nil] Key to be used for resource identification
|
38
|
+
#
|
39
|
+
# @return [PublisherMessage] published message
|
21
40
|
def publish(
|
22
41
|
topic_name:,
|
23
42
|
body:,
|
@@ -44,15 +63,26 @@ module Maitredee
|
|
44
63
|
message
|
45
64
|
end
|
46
65
|
|
66
|
+
# configure the adapter, must be executed before loading subscribers
|
67
|
+
#
|
68
|
+
# @param slug [#to_s] name of adapter
|
69
|
+
# @param args [] options to send to the adapter
|
47
70
|
def set_client(slug, *args)
|
48
71
|
raise "No client set for Maitredee" if slug.nil?
|
49
72
|
@client = "::Maitredee::Adapters::#{slug.to_s.camelize}Adapter".constantize.new(*args)
|
50
73
|
end
|
51
74
|
|
75
|
+
# set a client without parameters
|
76
|
+
#
|
77
|
+
# @param slug [#to_s] name of adapter
|
52
78
|
def client=(slug)
|
53
79
|
set_client(slug)
|
54
80
|
end
|
55
81
|
|
82
|
+
# build topic resource name from topic name
|
83
|
+
#
|
84
|
+
# @param topic_name [#to_s] topic name
|
85
|
+
# @return [String]
|
56
86
|
def topic_resource_name(topic_name)
|
57
87
|
[
|
58
88
|
namespace,
|
@@ -61,6 +91,11 @@ module Maitredee
|
|
61
91
|
].compact.join("--")
|
62
92
|
end
|
63
93
|
|
94
|
+
# build queue resource name from queue name and topic name
|
95
|
+
#
|
96
|
+
# @param topic_name [#to_s] topic name
|
97
|
+
# @param queue_name [#to_s] queue name
|
98
|
+
# @return [String]
|
64
99
|
def queue_resource_name(topic_name, queue_name)
|
65
100
|
[
|
66
101
|
namespace,
|
@@ -75,6 +110,13 @@ module Maitredee
|
|
75
110
|
end
|
76
111
|
end
|
77
112
|
|
113
|
+
# validate a body given a schema name
|
114
|
+
#
|
115
|
+
# @param body [Array, Hash, String] data to send with message
|
116
|
+
# @param schema [String] string key to look up schema to validate against
|
117
|
+
#
|
118
|
+
# @raise [ValidationError] if validation fails
|
119
|
+
# @return [nil]
|
78
120
|
def validate!(body, schema)
|
79
121
|
errors = schemas[schema].validate(body.as_json)
|
80
122
|
properties = errors.map do |error|
|
@@ -86,6 +128,9 @@ module Maitredee
|
|
86
128
|
end
|
87
129
|
end
|
88
130
|
|
131
|
+
# hash to look up schema based of schema_path
|
132
|
+
#
|
133
|
+
# @return Hash[JSONSchemer::Schema::Draft7]
|
89
134
|
def schemas
|
90
135
|
@schemas ||= Hash.new do |hash, key|
|
91
136
|
path = Pathname.new(schema_path).join("#{key}.json")
|
@@ -93,6 +138,10 @@ module Maitredee
|
|
93
138
|
end
|
94
139
|
end
|
95
140
|
|
141
|
+
# fetch configured app name or automatically fetch from Rails or from ENV["MAITREDEE_APP_NAME"]
|
142
|
+
# used for generating queue_resource_name
|
143
|
+
#
|
144
|
+
# @return [String]
|
96
145
|
def app_name
|
97
146
|
@app_name ||=
|
98
147
|
begin
|
@@ -106,11 +155,24 @@ module Maitredee
|
|
106
155
|
end
|
107
156
|
end
|
108
157
|
|
158
|
+
# set app_name instead of using default
|
159
|
+
# @param [String]
|
160
|
+
attr_writer :app_name
|
161
|
+
|
162
|
+
|
163
|
+
# fetch configured namespace or automatically fetch from ENV["MAITREDEE_NAMESPACE"]
|
164
|
+
# @return [String]
|
109
165
|
def namespace
|
110
166
|
@namespace ||=
|
111
167
|
ENV["MAITREDEE_NAMESPACE"] || raise("must set namespace for maitredee")
|
112
168
|
end
|
113
169
|
|
170
|
+
# set namespace instead of using default
|
171
|
+
# @param [String]
|
172
|
+
attr_writer :namespace
|
173
|
+
|
174
|
+
# idempotently configures broker to create topics, queues and subscribe queues to topics
|
175
|
+
# nothing will eveer be deleted or cleaned up
|
114
176
|
def configure_broker
|
115
177
|
hash_array = Hash.new { |hash, key| hash[key] = [] }
|
116
178
|
topics_and_queues =
|
@@ -121,11 +183,13 @@ module Maitredee
|
|
121
183
|
client.configure_broker(topics_and_queues)
|
122
184
|
end
|
123
185
|
|
186
|
+
# @api private
|
124
187
|
def register_subscriber(klass)
|
125
188
|
client.add_worker(klass)
|
126
189
|
subscriber_registry.add(klass)
|
127
190
|
end
|
128
191
|
|
192
|
+
# @api private
|
129
193
|
def subscriber_registry
|
130
194
|
@subscriber_registry ||= Set.new
|
131
195
|
end
|
data/lib/maitredee/active_job.rb
CHANGED
@@ -14,6 +14,16 @@ module Maitredee
|
|
14
14
|
subclass::PublisherJob.service_class = subclass
|
15
15
|
end
|
16
16
|
|
17
|
+
# Uses ActieJob to async the publishing
|
18
|
+
# @example To configure the specific async job open PublisherJob
|
19
|
+
# class RecipePublisher < Maitredee::Publisher
|
20
|
+
# class PublisherJob
|
21
|
+
# queue_as :low
|
22
|
+
# end
|
23
|
+
# end
|
24
|
+
#
|
25
|
+
# RecipePublisher.call_later(Recipe.find(1))
|
26
|
+
#
|
17
27
|
def call_later(*args)
|
18
28
|
self::PublisherJob.perform_later(*args)
|
19
29
|
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Maitredee
|
2
|
+
module Adapters
|
3
|
+
class BaseAdapter
|
4
|
+
def publish(message)
|
5
|
+
raise NotImplementedError
|
6
|
+
end
|
7
|
+
|
8
|
+
def add_worker(subscriber_class)
|
9
|
+
raise NotImplementedError
|
10
|
+
end
|
11
|
+
|
12
|
+
def reset
|
13
|
+
raise NotImplementedError
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
@@ -3,9 +3,15 @@ require "aws-sdk-sqs"
|
|
3
3
|
|
4
4
|
module Maitredee
|
5
5
|
module Adapters
|
6
|
-
class SnsSqsAdapter
|
6
|
+
class SnsSqsAdapter < BaseAdapter
|
7
7
|
attr_reader :access_key_id, :secret_access_key, :region
|
8
8
|
|
9
|
+
# @param access_key_id [String] if `nil` will look in `ENV["MAITREDEE_AWS_ACCESS_KEY_ID"]`
|
10
|
+
# @param secret_access_key [String] if `nil` will look in `ENV["MAITREDEE_AWS_SECRET_ACCESS_KEY"]`
|
11
|
+
# @param region [String] if `nil` will look in `ENV["MAITREDEE_AWS_REGION"]`
|
12
|
+
# @param default_shoryuken_options [Hash] default options of the shoryuken job listening to the queues
|
13
|
+
# defaults to `{ body_parser: :json, auto_delete: true }`
|
14
|
+
|
9
15
|
def initialize(access_key_id: nil, secret_access_key: nil, region: nil, default_shoryuken_options: nil)
|
10
16
|
@access_key_id = access_key_id || ENV["MAITREDEE_AWS_ACCESS_KEY_ID"]
|
11
17
|
@secret_access_key = secret_access_key || ENV["MAITREDEE_AWS_SECRET_ACCESS_KEY"]
|
@@ -15,6 +21,8 @@ module Maitredee
|
|
15
21
|
Shoryuken.sqs_client = sqs_client
|
16
22
|
end
|
17
23
|
|
24
|
+
# publishes message to SNS
|
25
|
+
# @param message [PublisherMessage]
|
18
26
|
def publish(message)
|
19
27
|
message_attributes = {
|
20
28
|
message_id: message.message_id,
|
@@ -32,6 +40,8 @@ module Maitredee
|
|
32
40
|
)
|
33
41
|
end
|
34
42
|
|
43
|
+
# creates topics from keys and queues from values, and subscribes queues to topics
|
44
|
+
# @param config [Hash{String => Array<String>}]
|
35
45
|
def configure_broker(config)
|
36
46
|
config.each do |topic_resource_name, queue_resource_names|
|
37
47
|
queue_resource_names.each do |queue_resource_name|
|
@@ -43,6 +53,7 @@ module Maitredee
|
|
43
53
|
end
|
44
54
|
end
|
45
55
|
|
56
|
+
# @api private
|
46
57
|
def topics
|
47
58
|
@topics ||= Hash.new do |hash, key|
|
48
59
|
topic = sns_client.create_topic(
|
@@ -52,6 +63,7 @@ module Maitredee
|
|
52
63
|
end
|
53
64
|
end
|
54
65
|
|
66
|
+
# @api private
|
55
67
|
def queues
|
56
68
|
@queues ||= Hash.new do |hash, key|
|
57
69
|
queue_url = sqs_client.create_queue(queue_name: key).queue_url
|
@@ -59,10 +71,14 @@ module Maitredee
|
|
59
71
|
end
|
60
72
|
end
|
61
73
|
|
74
|
+
# @api private
|
62
75
|
def subscriptions
|
63
76
|
@subscriptions ||= {}
|
64
77
|
end
|
65
78
|
|
79
|
+
# subscribes a queue to a topic
|
80
|
+
# @param queue_resource_name [String]
|
81
|
+
# @param topic_resource_name [String]
|
66
82
|
def subscribe(queue_resource_name:, topic_resource_name:)
|
67
83
|
topic = topics[topic_resource_name]
|
68
84
|
queue = queues[queue_resource_name]
|
@@ -102,6 +118,7 @@ module Maitredee
|
|
102
118
|
worker_class
|
103
119
|
end
|
104
120
|
|
121
|
+
# @api private
|
105
122
|
def default_shoryuken_options
|
106
123
|
@default_shoryuken_options ||= {
|
107
124
|
body_parser: :json,
|
@@ -109,6 +126,8 @@ module Maitredee
|
|
109
126
|
}
|
110
127
|
end
|
111
128
|
|
129
|
+
# deletes all topics, queues, and subscriptions
|
130
|
+
# @api private
|
112
131
|
def reset
|
113
132
|
[topics, queues, subscriptions].each do |resource|
|
114
133
|
resource.values.each(&:delete)
|
@@ -1,17 +1,21 @@
|
|
1
1
|
module Maitredee
|
2
2
|
module Adapters
|
3
|
-
class TestAdapter
|
3
|
+
class TestAdapter < BaseAdapter
|
4
|
+
# logs message published
|
4
5
|
def publish(message)
|
5
6
|
messages << message
|
6
7
|
end
|
7
8
|
|
9
|
+
# returns all messages that have been published since last #reset
|
8
10
|
def messages
|
9
11
|
@messages ||= []
|
10
12
|
end
|
11
13
|
|
14
|
+
# no-op
|
12
15
|
def add_worker(subscriber_class)
|
13
16
|
end
|
14
17
|
|
18
|
+
# resets messages logged
|
15
19
|
def reset
|
16
20
|
messages.clear
|
17
21
|
end
|
data/lib/maitredee/publisher.rb
CHANGED
@@ -1,12 +1,37 @@
|
|
1
1
|
module Maitredee
|
2
|
+
##
|
3
|
+
# Inherit from this class to easily publish messages:
|
4
|
+
#
|
5
|
+
# class RecipePublisher < Maitredee::Publisher
|
6
|
+
#
|
7
|
+
# def initialize(recipe)
|
8
|
+
# @recipe = recipe
|
9
|
+
# end
|
10
|
+
#
|
11
|
+
# def process
|
12
|
+
# # do some work
|
13
|
+
# end
|
14
|
+
# end
|
15
|
+
#
|
16
|
+
# Then in your Rails app, you can do this:
|
17
|
+
#
|
18
|
+
# RecipePublisher.call(1, 2, 3)
|
19
|
+
#
|
20
|
+
# Note that `call` is a class method, `process` is an instance method.
|
2
21
|
class Publisher
|
3
22
|
class << self
|
23
|
+
# call #process and return publishes messages
|
24
|
+
# @param args [] arguments passed to #initialize
|
4
25
|
def call(*args)
|
5
26
|
publisher = new(*args)
|
6
27
|
publisher.process
|
7
28
|
publisher.published_messages
|
8
29
|
end
|
9
30
|
|
31
|
+
# set publish defaults
|
32
|
+
# @param topic_name [#to_s, nil]
|
33
|
+
# @param event_name [#to_s, nil]
|
34
|
+
# @param schema_name [#to_s, nil]
|
10
35
|
def publish_defaults(topic_name: nil, event_name: nil, schema_name: nil)
|
11
36
|
@publish_defaults = {
|
12
37
|
topic_name: topic_name,
|
@@ -20,10 +45,18 @@ module Maitredee
|
|
20
45
|
end
|
21
46
|
end
|
22
47
|
|
48
|
+
# array of messages published in this instance
|
49
|
+
# @return [Array<PublisherMessage>]
|
23
50
|
def published_messages
|
24
51
|
@published_messages ||= []
|
25
52
|
end
|
26
53
|
|
54
|
+
# publish a message with defaults
|
55
|
+
# @param topic_name [#to_s, nil]
|
56
|
+
# @param event_name [#to_s, nil]
|
57
|
+
# @param schema_name [#to_s, nil]
|
58
|
+
# @param primary_key [#to_s, nil]
|
59
|
+
# @param body [#to_json]
|
27
60
|
def publish(topic_name: nil, event_name: nil, schema_name: nil, primary_key: nil, body:)
|
28
61
|
defaults = self.class.get_publish_defaults
|
29
62
|
published_messages << Maitredee.publish(
|
data/lib/maitredee/subscriber.rb
CHANGED
@@ -1,11 +1,28 @@
|
|
1
1
|
require "shoryuken"
|
2
2
|
|
3
3
|
module Maitredee
|
4
|
+
##
|
5
|
+
# Inherit from this class to easily subscrive to messages:
|
6
|
+
#
|
7
|
+
# class RecipeSubscriber < Maitredee::Subscriber
|
8
|
+
# subscribe_to :recipes do
|
9
|
+
# event(:update) # by default this calls the event_name, #delete
|
10
|
+
# end
|
11
|
+
#
|
12
|
+
# def update
|
13
|
+
# # do some work
|
14
|
+
# end
|
15
|
+
# end
|
16
|
+
#
|
17
|
+
# If you want to process a message manually
|
18
|
+
#
|
19
|
+
# RecipePublisher.process()
|
20
|
+
#
|
21
|
+
# Note that `call` is a class method, `process` is an instance method.
|
4
22
|
class Subscriber
|
5
23
|
EventConfig = Struct.new(
|
6
24
|
:action,
|
7
25
|
:event_name,
|
8
|
-
:minimum_schema,
|
9
26
|
keyword_init: true
|
10
27
|
)
|
11
28
|
|
@@ -16,25 +33,33 @@ module Maitredee
|
|
16
33
|
@subscriber = subscriber
|
17
34
|
end
|
18
35
|
|
19
|
-
|
36
|
+
# configure subscriber to listen to event_name
|
37
|
+
# @param event_name [nil, #to_s]
|
38
|
+
# @param to [#to_s] must be valid method name
|
39
|
+
def event(event_name, to: nil)
|
20
40
|
if event_name.nil? && to.nil?
|
21
41
|
raise ArgumentError, "event_name and to: cannot both be nil"
|
22
42
|
end
|
23
43
|
|
44
|
+
if event_name.present? && /[@$"]/ =~ event_name.to_sym.inspect && to.nil?
|
45
|
+
raise ArgumentError, "'#{event_name}' is not a valid method name, you must set the 'to' parameter"
|
46
|
+
end
|
47
|
+
|
24
48
|
event_config = EventConfig.new(
|
25
49
|
event_name: event_name.to_s,
|
26
|
-
action: (to || event_name).to_s
|
27
|
-
minimum_schema: minimum_schema
|
50
|
+
action: (to || event_name).to_s
|
28
51
|
)
|
29
52
|
|
30
53
|
subscriber.event_configs[event_config.event_name] = event_config
|
31
54
|
end
|
32
55
|
|
33
|
-
|
56
|
+
# configure a default method to be called if not specifically configured to be listened to
|
57
|
+
# @param event_name [#to_s]
|
58
|
+
# @param to [#to_sym] must be valid method name
|
59
|
+
def default_event(to:)
|
34
60
|
subscriber.event_configs.default = EventConfig.new(
|
35
61
|
event_name: nil,
|
36
|
-
action: to.to_s
|
37
|
-
minimum_schema: minimum_schema
|
62
|
+
action: to.to_s
|
38
63
|
)
|
39
64
|
end
|
40
65
|
end
|
@@ -42,6 +67,30 @@ module Maitredee
|
|
42
67
|
class << self
|
43
68
|
attr_reader :topic_name
|
44
69
|
|
70
|
+
# configures Subscriber to which topic it should listen to and lets you configure events in the block
|
71
|
+
# @example subscribe to a topic
|
72
|
+
# class RecipeSubscriber < Maitredee::Subscriber
|
73
|
+
# subscribe_to :recipes do
|
74
|
+
# event(:delete) # by default this calls the event_name, #delete
|
75
|
+
# event(:update, to: :process) # events can be routed to different methods though
|
76
|
+
# event(nil, to: :process) # subscribe without event names
|
77
|
+
#
|
78
|
+
# default_event(to: :process) # this will default a catch all route
|
79
|
+
# end
|
80
|
+
#
|
81
|
+
# def delete
|
82
|
+
# # do some work
|
83
|
+
# end
|
84
|
+
#
|
85
|
+
# def process
|
86
|
+
# # do some work
|
87
|
+
# end
|
88
|
+
# end
|
89
|
+
#
|
90
|
+
# @see SubscriberProxy
|
91
|
+
# @param topic_name [#to_s]
|
92
|
+
# @param queue_name [#to_s] overide default generation from class name
|
93
|
+
# @param queue_resource_name [#to_s] overide default generation from queue_name and topic_name
|
45
94
|
def subscribe_to(topic_name, queue_name: nil, queue_resource_name: nil, &block)
|
46
95
|
@topic_name = topic_name
|
47
96
|
@queue_name = queue_name if queue_name
|
@@ -57,18 +106,24 @@ module Maitredee
|
|
57
106
|
Maitredee.register_subscriber(self)
|
58
107
|
end
|
59
108
|
|
109
|
+
# @api private
|
60
110
|
def event_configs
|
61
111
|
@event_configs ||= {}
|
62
112
|
end
|
63
113
|
|
114
|
+
# returns the queue_name set in .subscribe_to or is generated off the class name without `Subscriber`
|
64
115
|
def queue_name
|
65
116
|
@queue_name ||= name.chomp(Subscriber.name.demodulize).underscore.dasherize
|
66
117
|
end
|
67
118
|
|
119
|
+
# Returns the resource name of the queue depending on the adapter
|
120
|
+
# @return [String]
|
68
121
|
def queue_resource_name
|
69
122
|
@queue_resource_name ||= Maitredee.queue_resource_name(topic_name, queue_name)
|
70
123
|
end
|
71
124
|
|
125
|
+
# takes message and routes it based off SubscriberMessage#event_name
|
126
|
+
# @param message [SubscriberMessage]
|
72
127
|
def process(message)
|
73
128
|
event_config = event_configs[message.event_name.to_s]
|
74
129
|
if event_config
|
@@ -77,8 +132,10 @@ module Maitredee
|
|
77
132
|
end
|
78
133
|
end
|
79
134
|
|
135
|
+
# @return [SubscriberMessage]
|
80
136
|
attr_reader :message
|
81
137
|
|
138
|
+
# @param message [SubscriberMessage]
|
82
139
|
def initialize(message)
|
83
140
|
@message = message
|
84
141
|
end
|
data/lib/maitredee/version.rb
CHANGED
data/maitredee.gemspec
CHANGED
@@ -32,6 +32,7 @@ Gem::Specification.new do |spec|
|
|
32
32
|
|
33
33
|
spec.add_development_dependency "bundler", "~> 1.17"
|
34
34
|
spec.add_development_dependency "dotenv"
|
35
|
+
spec.add_development_dependency "goodread"
|
35
36
|
spec.add_development_dependency "pry"
|
36
37
|
spec.add_development_dependency "pry-byebug"
|
37
38
|
spec.add_development_dependency "rake", "~> 10.0"
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: maitredee
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.8.
|
4
|
+
version: 0.8.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Plated Devs
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-12-
|
11
|
+
date: 2018-12-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -122,6 +122,20 @@ dependencies:
|
|
122
122
|
- - ">="
|
123
123
|
- !ruby/object:Gem::Version
|
124
124
|
version: '0'
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: goodread
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - ">="
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '0'
|
132
|
+
type: :development
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - ">="
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '0'
|
125
139
|
- !ruby/object:Gem::Dependency
|
126
140
|
name: pry
|
127
141
|
requirement: !ruby/object:Gem::Requirement
|
@@ -232,6 +246,7 @@ files:
|
|
232
246
|
- ".gitignore"
|
233
247
|
- ".rspec"
|
234
248
|
- ".ruby-version"
|
249
|
+
- ".yardopts"
|
235
250
|
- CODE_OF_CONDUCT.md
|
236
251
|
- Gemfile
|
237
252
|
- Gemfile.lock
|
@@ -243,6 +258,7 @@ files:
|
|
243
258
|
- exe/maitredee
|
244
259
|
- lib/maitredee.rb
|
245
260
|
- lib/maitredee/active_job.rb
|
261
|
+
- lib/maitredee/adapters/base_adapter.rb
|
246
262
|
- lib/maitredee/adapters/sns_sqs_adapter.rb
|
247
263
|
- lib/maitredee/adapters/test_adapter.rb
|
248
264
|
- lib/maitredee/cli/runner.rb
|
@@ -271,7 +287,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
271
287
|
version: '0'
|
272
288
|
requirements: []
|
273
289
|
rubyforge_project:
|
274
|
-
rubygems_version: 2.7.
|
290
|
+
rubygems_version: 2.7.6
|
275
291
|
signing_key:
|
276
292
|
specification_version: 4
|
277
293
|
summary: Opinionated pub/sub framework
|