maitredee 0.8.2 → 0.8.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|