klunk 0.1.0
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 +7 -0
- data/.gitignore +34 -0
- data/.rspec +2 -0
- data/.rubocop.yml +11 -0
- data/Gemfile +8 -0
- data/README.md +162 -0
- data/Rakefile +8 -0
- data/klunk.gemspec +29 -0
- data/lib/generators/klunk/USAGE +4 -0
- data/lib/generators/klunk/install_generator.rb +21 -0
- data/lib/generators/klunk/templates/config/initializers/klunk.rb +17 -0
- data/lib/generators/klunk/templates/config/queues.yml +19 -0
- data/lib/generators/klunk/templates/config/topics.yml +7 -0
- data/lib/klunk/configuration.rb +7 -0
- data/lib/klunk/queue.rb +86 -0
- data/lib/klunk/railtie.rb +18 -0
- data/lib/klunk/tasks/klunk.rake +25 -0
- data/lib/klunk/topic.rb +88 -0
- data/lib/klunk/version.rb +3 -0
- data/lib/klunk.rb +22 -0
- data/vulture_squadron.jpg +0 -0
- metadata +149 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 6414ef36d0b3146def7a1217ceae1134f0e02fbe
|
4
|
+
data.tar.gz: 08c4f0c806ebbb3be0ef62bd30966f8b86fa6883
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 225e0c1d2fc3233f3a63fe31b4f5f4cd2ac0cf44725b0f15914eb416dec171e993297ee65daf01061c8c75c932a40b0f6628cbde6cf06327c8a634cd624fd763
|
7
|
+
data.tar.gz: 2bec47db4358322c5139f826a1b9f439b1179e4c61d0cd30eba0654779aff2c42f39d7a90054927733048032bc2a4041b233132ef1e39f0b74f262b7f8da80ee
|
data/.gitignore
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
*.gem
|
2
|
+
*.rbc
|
3
|
+
/.config
|
4
|
+
/coverage/
|
5
|
+
/InstalledFiles
|
6
|
+
/pkg/
|
7
|
+
/spec/reports/
|
8
|
+
/spec/examples.txt
|
9
|
+
/test/tmp/
|
10
|
+
/test/version_tmp/
|
11
|
+
/tmp/
|
12
|
+
|
13
|
+
# Used by dotenv library to load environment variables.
|
14
|
+
.env
|
15
|
+
|
16
|
+
## Documentation cache and generated files:
|
17
|
+
/.yardoc/
|
18
|
+
/_yardoc/
|
19
|
+
/doc/
|
20
|
+
/rdoc/
|
21
|
+
|
22
|
+
## Environment normalization:
|
23
|
+
/.bundle/
|
24
|
+
/vendor/bundle
|
25
|
+
/lib/bundler/man/
|
26
|
+
|
27
|
+
# for a library or gem, you might want to ignore these files since the code is
|
28
|
+
# intended to run in multiple environments; otherwise, check them in:
|
29
|
+
Gemfile.lock
|
30
|
+
.ruby-version
|
31
|
+
.ruby-gemset
|
32
|
+
|
33
|
+
# unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
|
34
|
+
.rvmrc
|
data/.rspec
ADDED
data/.rubocop.yml
ADDED
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,162 @@
|
|
1
|
+
# Klunk
|
2
|
+
|
3
|
+
## Goal
|
4
|
+
|
5
|
+
Create and setup __publishers__ (topics) and __subscribers__ (queues) for Rails applications.
|
6
|
+
|
7
|
+
## About
|
8
|
+
|
9
|
+
"Stop the pigeon! Stop the pigeon!"
|
10
|
+
|
11
|
+
Dick Dastardly and Muttley, the villains from Wacky Races, are now flying aces and members of the Vulture Squadron, a crew of aviators on a mission to stop a homing pigeon named _Yankee Doodle Pigeon_ from delivering _messages_ to the other side. Each story features variations on the same plot elements: the Vulture Squadron tries to trap _Yankee Doodle Pigeon_ using one or more planes equipped with __Klunk__'s latest contraptions, but one or more of the Squadron messes up and the planes either crash, collide or explode (or all of the above).
|
12
|
+
|
13
|
+

|
14
|
+
|
15
|
+
## Publish-Subscribe Messaging Pattern
|
16
|
+
|
17
|
+
In software architecture, __publish–subscribe__ is a messaging pattern where senders of messages, called _publishers_, do not program the messages to be sent directly to specific receivers, called _subscribers_, but instead characterize published messages into classes without knowledge of which _subscribers_, if any, there may be. Similarly, _subscribers_ express interest in one or more classes and only receive messages that are of interest, without knowledge of which _publishers_, if any, there are.
|
18
|
+
|
19
|
+
__Publish–Subscribe__ is a sibling of the message queue paradigm, and is typically one part of a larger message-oriented middleware system. This pattern provides greater network scalability and a more dynamic network topology, with a resulting decreased flexibility to modify the publisher and the structure of the published data. In other words, __publish-subscribe__ is a pattern used to communicate messages between different system components without these components knowing anything about each other’s identity.
|
20
|
+
|
21
|
+
This design pattern is not new, but it’s not commonly used by Rails developers. But it can bring major advantages for a Rails application.
|
22
|
+
|
23
|
+
### Advantages
|
24
|
+
|
25
|
+
* __Reduce model/controller bloat__
|
26
|
+
|
27
|
+
It can help decomposing fat models or controllers.
|
28
|
+
|
29
|
+
* __Fewer callbacks__
|
30
|
+
|
31
|
+
Enable models to work independently with minimum knowledge about each other, ensuring loose coupling. Expanding the behavior to additional actions is just a matter of hooking to the desired event.
|
32
|
+
|
33
|
+
* __Single Responsibility Principle (SRP)__
|
34
|
+
|
35
|
+
_Models_ should handle persistence, associations and not much else.
|
36
|
+
_Controllers_ should handle user requests and be a wrapper around the business logic (Service Objects).
|
37
|
+
Service Objects should encapsulate one of the responsibilities of the business logic, provide an entry point for external services, or act as an alternative to model concerns.
|
38
|
+
Thanks to its power to reduce coupling, the __publish-subscribe__ design pattern can be combined with single responsibility service objects (SRSOs) to help encapsulate the business logic, and forbid the business logic from creeping into either the models or the controllers. This keeps the code base clean, readable, maintainable and scalable.
|
39
|
+
|
40
|
+
* __Testing__
|
41
|
+
|
42
|
+
By decomposing the fat models and controllers, and having a lot of SRSOs, testing of the code base becomes a much, much easier process. This is particularly the case when it comes to integration testing and inter-module communication. Testing should simply ensure that events are published and received correctly.
|
43
|
+
|
44
|
+
### Disadvantages
|
45
|
+
|
46
|
+
* _Loose coupling_
|
47
|
+
|
48
|
+
The greatest of the __publish-subscribe__ pattern’s strength may be also it’s greatest weakness. The structure of the data published (the event payload) must be well defined, and quickly becomes rather inflexible. In order to modify data structure of the published payload, it is necessary to know about all the _subscribers_, and either modify them also, or ensure the modifications are compatible with older versions. This makes refactoring of _publisher_ code much more difficult.
|
49
|
+
If you want to avoid this you have to be extra cautious when defining the payload of the _publishers_. Of course, if you have a great test suite, that tests the payload as well as mentioned previously, you don’t have to worry much about the system going down after you change the _publisher_’s payload or event name.
|
50
|
+
|
51
|
+
* _Messaging Bus stability_
|
52
|
+
|
53
|
+
_Publishers_ have no knowledge of the status of the _subscriber_ and vice versa. Using simple __publish-subscribe__ tools, it might not be possible to ensure the stability of the messaging bus itself, and to ensure that all the published messages are correctly queued and delivered.
|
54
|
+
|
55
|
+
* _Visibility_
|
56
|
+
|
57
|
+
Eventually, messages can cause receiver processes (workers) to crash. Specify maximum retry limits and message hospital (dead letter queues), where messages got sent if they failed. And, in order to avoid a catastrophic failover, having an UI to monitor those messages and retry them if needed is strongly encouraged.
|
58
|
+
|
59
|
+
* _Infinite event loops_
|
60
|
+
|
61
|
+
When the system is completely driven by events, you should be extra cautious not to have event loops. These loops are just like the infinite loops that can happen in code. However, they are harder to detect ahead of time, and they can bring your system to a standstill. They can exist without your notice when there are many events published and subscribed across the system.
|
62
|
+
|
63
|
+
* __Setup__
|
64
|
+
|
65
|
+
When you split the monolith and move towards fine grained systems, the setup and deployment of _publishers_ and _subscribers_ can be painful and error prone. This is why we created __Klunk__.
|
66
|
+
|
67
|
+
## Installation
|
68
|
+
|
69
|
+
Add it to your `Gemfile`:
|
70
|
+
|
71
|
+
```ruby
|
72
|
+
gem 'klunk'
|
73
|
+
```
|
74
|
+
|
75
|
+
Run the following command to install it:
|
76
|
+
|
77
|
+
```console
|
78
|
+
bundle install
|
79
|
+
```
|
80
|
+
|
81
|
+
Run the generator:
|
82
|
+
|
83
|
+
```console
|
84
|
+
rails generate klunk:install
|
85
|
+
```
|
86
|
+
|
87
|
+
## Configuration
|
88
|
+
|
89
|
+
### Common
|
90
|
+
|
91
|
+
Check your `config/initializers/klunk.rb` file for basic and default values.
|
92
|
+
|
93
|
+
- `prefix`: organize names by using a prefix, usually the system name. Defaults to current Rails application name.
|
94
|
+
- `deadletter_suffix`: suffix for dead letter queue names. Default value is `failures`.
|
95
|
+
- `retries_limit`: the number of times that a message can be received before being sent to a dead letter queue. Must be between 1 and 1000. Default is 8.
|
96
|
+
- `message_retention_period`: The number of seconds for which the queue should retain a message. An integer representing seconds, from 60 (1 minute) to 1209600 (14 days). The default is 345600 (4 days).
|
97
|
+
- `deadletter_message_retention_period`: Same as above, for the corresponding dead letter queue. It's a good practice to keep the failed messages as long as you can to be able to retry them, so the default value is 14 days.
|
98
|
+
|
99
|
+
#### Notes
|
100
|
+
|
101
|
+
- Queue names and queue URLs are case-sensitive
|
102
|
+
|
103
|
+
### Queues
|
104
|
+
|
105
|
+
Use the file `config/queues.yml` to define your application queues.
|
106
|
+
|
107
|
+
- `name`: the queue name is mandatory. This value will be used together with the application and environment names to build the final queue name. Example: `klunk_development_queue` and `klunk_development_queue_failures`.
|
108
|
+
- `retries_limit`: optional, if set, this value will override the default maximum retries.
|
109
|
+
- `subscribes`: the topics this queue subscribes to (optional).
|
110
|
+
- `name`: the topic name (required). The topic name will be built like queue names (based on prefix and environment values unless `prefix`is set to false, as described below.
|
111
|
+
- `prefix`: boolean flag to decide if the topic name should be built like the queues naming convention. If set to `false`, the full topic name must be provided. Optional, defaults to `true`.
|
112
|
+
- `system`: optional, used as prefix for external system name. Defaults to the current global `prefix` name.
|
113
|
+
|
114
|
+
|
115
|
+
```yaml
|
116
|
+
|
117
|
+
-
|
118
|
+
:name: queue_one
|
119
|
+
-
|
120
|
+
:name: queue_two
|
121
|
+
:retries_limit: 3
|
122
|
+
-
|
123
|
+
:name: queue_three
|
124
|
+
:subscribes:
|
125
|
+
-
|
126
|
+
:system: service_one
|
127
|
+
:name: topic_one
|
128
|
+
-
|
129
|
+
:system: service_one
|
130
|
+
:name: topic_two
|
131
|
+
-
|
132
|
+
:prefix: false
|
133
|
+
:name: full_topic_name
|
134
|
+
|
135
|
+
```
|
136
|
+
|
137
|
+
### Topics
|
138
|
+
|
139
|
+
TODO: Explain how to setup - configuration files, values and so on...
|
140
|
+
|
141
|
+
## Usage
|
142
|
+
|
143
|
+
TODO: Write usage instructions here - explain the `Rake` tasks...
|
144
|
+
|
145
|
+
## Disclaimer
|
146
|
+
|
147
|
+
So far, __Klunk__ only works with Amazon [SNS](https://aws.amazon.com/sns/) and [SQS](https://aws.amazon.com/sqs/) services. But you shouldn't be using anything else, right?! :wink:
|
148
|
+
|
149
|
+
## Maintainers
|
150
|
+
|
151
|
+
* Marco Antonio Gonzalez Junior (https://github.com/kayaman)
|
152
|
+
* Wagner Vaz (https://github.com/0xdco)
|
153
|
+
|
154
|
+
## Contributing
|
155
|
+
|
156
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/kayaman/klunk.
|
157
|
+
|
158
|
+
## License
|
159
|
+
|
160
|
+
__Klunk__ is an Open Source project licensed under the terms of
|
161
|
+
the LGPLv3 license. Please see [http://www.gnu.org/licenses/lgpl-3.0.html](http://www.gnu.org/licenses/lgpl-3.0.html)
|
162
|
+
for license text.
|
data/Rakefile
ADDED
data/klunk.gemspec
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'klunk/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = 'klunk'
|
8
|
+
spec.version = Klunk::VERSION
|
9
|
+
spec.authors = ['Marco Antonio Gonzalez Junior', 'Wagner Vaz']
|
10
|
+
spec.email = ['kayaman.baurets@gmail.com', 'wv@0xd.co']
|
11
|
+
|
12
|
+
spec.summary = 'Enterprise Messaging System on Rails'
|
13
|
+
spec.description = 'Enterprise Messaging System on Rails'
|
14
|
+
spec.homepage = 'https://github.com/kayaman/klunk'
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
17
|
+
f.match(%r{^(test|spec|features)/})
|
18
|
+
end
|
19
|
+
|
20
|
+
spec.require_paths = ['lib']
|
21
|
+
|
22
|
+
spec.add_runtime_dependency 'aws-sdk', '~> 2'
|
23
|
+
spec.add_runtime_dependency 'safe_yaml'
|
24
|
+
spec.add_runtime_dependency 'json'
|
25
|
+
|
26
|
+
spec.add_development_dependency 'bundler', '~> 1.13'
|
27
|
+
spec.add_development_dependency 'rake', '~> 10.0'
|
28
|
+
spec.add_development_dependency 'rspec', '~> 3.0'
|
29
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Klunk
|
2
|
+
module Generators
|
3
|
+
class InstallGenerator < Rails::Generators::Base
|
4
|
+
desc 'Creates Klunk initializer for your application'
|
5
|
+
source_root File.expand_path('../templates', __FILE__)
|
6
|
+
|
7
|
+
def copy_initializer
|
8
|
+
template 'config/initializers/klunk.rb'
|
9
|
+
puts 'Install complete! Check \'config/initializers/klunk.rb\'.'
|
10
|
+
end
|
11
|
+
|
12
|
+
def copy_topics_configuration_file
|
13
|
+
template 'config/topics.yml'
|
14
|
+
end
|
15
|
+
|
16
|
+
def copy_queues_configuration_file
|
17
|
+
template 'config/queues.yml'
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
Klunk.configure do |config|
|
2
|
+
# Organize names by using a prefix, usually the project name
|
3
|
+
config.prefix = Rails.application.class.parent_name.downcase if defined? Rails
|
4
|
+
|
5
|
+
# Message hospital: suffix for dead letter queue names
|
6
|
+
config.deadletter_suffix = 'failures'
|
7
|
+
|
8
|
+
# Message hospital: maximum retries limit
|
9
|
+
config.retries_limit = 8
|
10
|
+
|
11
|
+
# The number of seconds for which Amazon SQS retains a message. An integer
|
12
|
+
# representing seconds, from 60 (1 minute) to 120,9600 (14 days).
|
13
|
+
# The default is 345,600 (4 days).
|
14
|
+
config.message_retention_period = 345_600
|
15
|
+
|
16
|
+
config.deadletter_message_retention_period = 1_209_600
|
17
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# Define your Amazon SQS queues and subscriptions here
|
2
|
+
|
3
|
+
# Examples:
|
4
|
+
# -
|
5
|
+
# :name: queue_one
|
6
|
+
#
|
7
|
+
# -
|
8
|
+
# :name: queue_two
|
9
|
+
# :retries_limit: 3
|
10
|
+
#
|
11
|
+
# -
|
12
|
+
# :name: queue_three
|
13
|
+
# :retries_limit: 8
|
14
|
+
# :subscribes:
|
15
|
+
# -
|
16
|
+
# :name: topic_one
|
17
|
+
# -
|
18
|
+
# :system: some_other_system_name
|
19
|
+
# :name: topic_two
|
data/lib/klunk/queue.rb
ADDED
@@ -0,0 +1,86 @@
|
|
1
|
+
module Klunk
|
2
|
+
class Queue
|
3
|
+
QUEUES = YAML.load_file('config/queues.yml').map(&:deep_symbolize_keys)
|
4
|
+
|
5
|
+
class << self
|
6
|
+
def build(queue_options)
|
7
|
+
queue_name = queue_options.delete(:name)
|
8
|
+
subscriptions = queue_options.delete(:subscribes)
|
9
|
+
deadletter_queue = create_deadletter(queue_name)
|
10
|
+
deadletter_attributes = get_attributes(deadletter_queue[:queue_url])
|
11
|
+
attributes = build_attributes(queue_options)
|
12
|
+
attributes[:RedrivePolicy][:deadLetterTargetArn] =
|
13
|
+
deadletter_attributes['QueueArn']
|
14
|
+
attributes[:RedrivePolicy] = attributes[:RedrivePolicy].to_json
|
15
|
+
create(queue_name, attributes, subscriptions)
|
16
|
+
end
|
17
|
+
|
18
|
+
def create(queue_name, attributes, subscriptions)
|
19
|
+
begin
|
20
|
+
queue = client.create_queue(
|
21
|
+
queue_name: name_for(queue_name),
|
22
|
+
attributes: attributes
|
23
|
+
)
|
24
|
+
rescue Aws::SQS::Errors::QueueAlreadyExists
|
25
|
+
puts "#{queue_name} already exists.".green
|
26
|
+
queue = client.create_queue(queue_name: name_for(queue_name))
|
27
|
+
end
|
28
|
+
subscriptions.to_a.each do |subscription|
|
29
|
+
queue_arn = attributes['QueueArn']
|
30
|
+
topic_name = Topic.name_for(subscription[:name], subscription)
|
31
|
+
topic = Topic.create(topic_name)
|
32
|
+
ap Topic.subscribe(queue.queue_url, topic.topic_arn)
|
33
|
+
end
|
34
|
+
queue
|
35
|
+
end
|
36
|
+
|
37
|
+
def get_attributes(queue_url, attribute_names = ['All'])
|
38
|
+
attributes = client.get_queue_attributes(
|
39
|
+
queue_url: queue_url,
|
40
|
+
attribute_names: attribute_names
|
41
|
+
)
|
42
|
+
attributes[:attributes]
|
43
|
+
end
|
44
|
+
|
45
|
+
def name_for(queue_name, deadletter = false)
|
46
|
+
name = [Klunk.configuration.prefix, ENV['EB_ENV'], queue_name]
|
47
|
+
name << Klunk.configuration.deadletter_suffix if deadletter
|
48
|
+
name.compact.reject(&:blank?).join('_')
|
49
|
+
end
|
50
|
+
|
51
|
+
def create_deadletter(queue_name)
|
52
|
+
client.create_queue(
|
53
|
+
queue_name: name_for(queue_name, true),
|
54
|
+
attributes: {
|
55
|
+
MessageRetentionPeriod: deadletter_message_retention_period.to_s
|
56
|
+
}
|
57
|
+
)
|
58
|
+
end
|
59
|
+
|
60
|
+
def build_attributes(queue, attributes = {})
|
61
|
+
max_receive_count = queue[:retries_limit] ||
|
62
|
+
Klunk.configuration.retries_limit
|
63
|
+
message_retention_period = queue[:message_retention_period] ||
|
64
|
+
Klunk.configuration.message_retention_period
|
65
|
+
{
|
66
|
+
MessageRetentionPeriod: message_retention_period.to_s,
|
67
|
+
RedrivePolicy: {
|
68
|
+
maxReceiveCount: max_receive_count
|
69
|
+
}.merge(attributes)
|
70
|
+
}
|
71
|
+
end
|
72
|
+
|
73
|
+
def client
|
74
|
+
@client ||= Aws::SQS::Client.new
|
75
|
+
end
|
76
|
+
|
77
|
+
def resource
|
78
|
+
Aws::SQS::Resource.new(client: client)
|
79
|
+
end
|
80
|
+
|
81
|
+
def deadletter_message_retention_period
|
82
|
+
Klunk.configuration.deadletter_message_retention_period
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'rails/railtie'
|
2
|
+
|
3
|
+
module Klunk
|
4
|
+
class Railtie < Rails::Railtie
|
5
|
+
config.eager_load_namespaces << Klunk
|
6
|
+
|
7
|
+
config.after_initialize do
|
8
|
+
unless Klunk.configured?
|
9
|
+
warn '[Klunk] Klunk is not configured and will use the default values.\
|
10
|
+
Use `rails generate klunk:install` and set it up.'
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
rake_tasks do
|
15
|
+
load "#{File.expand_path('../tasks', __FILE__)}/klunk.rake"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
namespace :klunk do
|
2
|
+
namespace :sns do
|
3
|
+
desc 'Create SNS topics'
|
4
|
+
task create_topics: :environment do
|
5
|
+
Klunk::Topic::TOPICS.each do |topic|
|
6
|
+
puts Klunk::Topic.create(topic[:name])
|
7
|
+
end
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
namespace :sqs do
|
12
|
+
desc 'Create SQS queues if needed'
|
13
|
+
task create_sqs_queues_if_needed: :environment do
|
14
|
+
# TODO: help me
|
15
|
+
end
|
16
|
+
|
17
|
+
desc 'Create SQS queues'
|
18
|
+
task create_queues: :environment do
|
19
|
+
Klunk::Queue::QUEUES.each do |queue|
|
20
|
+
q = Klunk::Queue.build(queue)
|
21
|
+
puts "\n#{q[:queue_url]}"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
data/lib/klunk/topic.rb
ADDED
@@ -0,0 +1,88 @@
|
|
1
|
+
module Klunk
|
2
|
+
class Topic
|
3
|
+
TOPICS = YAML.load_file('config/topics.yml').map(&:deep_symbolize_keys)
|
4
|
+
|
5
|
+
class << self
|
6
|
+
def name_for(topic_name, options = {})
|
7
|
+
system_name = options[:system] || Klunk.configuration.prefix
|
8
|
+
[system_name, ENV['EB_ENV'], topic_name]
|
9
|
+
.compact.reject(&:blank?).join('_')
|
10
|
+
end
|
11
|
+
|
12
|
+
def create(topic_name)
|
13
|
+
topic = client.create_topic(name: topic_name)
|
14
|
+
puts "Topic: #{topic.topic_arn}".cyan
|
15
|
+
topic
|
16
|
+
end
|
17
|
+
|
18
|
+
def subscribe(queue_url, topic_arn, previous_policy = nil)
|
19
|
+
queue_attributes = Klunk::Queue.get_attributes(queue_url)
|
20
|
+
queue_arn = queue_attributes['QueueArn']
|
21
|
+
subscription = client.subscribe(
|
22
|
+
topic_arn: topic_arn,
|
23
|
+
protocol: 'sqs',
|
24
|
+
endpoint: queue_arn)
|
25
|
+
client.set_subscription_attributes(
|
26
|
+
subscription_arn: subscription.subscription_arn,
|
27
|
+
attribute_name: 'RawMessageDelivery',
|
28
|
+
attribute_value: 'true')
|
29
|
+
if queue_attributes.key?('Policy')
|
30
|
+
previous_policy = JSON.parse(queue_attributes['Policy'])
|
31
|
+
end
|
32
|
+
add_policy(queue_url, topic_arn, previous_policy)
|
33
|
+
topic_subscriptions = {
|
34
|
+
topic: "#{topic_arn}",
|
35
|
+
subscriptions: client.list_subscriptions_by_topic(
|
36
|
+
topic_arn: topic_arn
|
37
|
+
).subscriptions.map {|topic| topic[:endpoint] }
|
38
|
+
}
|
39
|
+
end
|
40
|
+
|
41
|
+
def add_policy(queue_url, topic_arn, previous_policy)
|
42
|
+
previous_policy ||= build_policy(queue_url, topic_arn)
|
43
|
+
Queue.client.set_queue_attributes(
|
44
|
+
queue_url: queue_url,
|
45
|
+
attributes: {
|
46
|
+
Policy: previous_policy.tap do |p|
|
47
|
+
p['Statement'] ||= []
|
48
|
+
p['Statement'] << build_statement(queue_url, topic_arn)
|
49
|
+
p['Statement'].uniq!
|
50
|
+
end.to_json
|
51
|
+
}
|
52
|
+
)
|
53
|
+
end
|
54
|
+
|
55
|
+
def build_statement(queue_url, topic_arn)
|
56
|
+
queue_arn = Klunk::Queue.get_attributes(queue_url)['QueueArn']
|
57
|
+
queue_name = queue_arn.split(':').last
|
58
|
+
topic = client.get_topic_attributes(topic_arn: topic_arn)
|
59
|
+
topic_name = topic_arn.split(':').last
|
60
|
+
{
|
61
|
+
'Sid': "#{queue_name.camelize}_Send_#{topic_name.camelize}",
|
62
|
+
'Effect': 'Allow',
|
63
|
+
'Principal': { 'AWS': '*' },
|
64
|
+
'Action': 'SQS:SendMessage',
|
65
|
+
'Resource': queue_arn,
|
66
|
+
'Condition': {
|
67
|
+
'ArnEquals': { 'aws:SourceArn': topic_arn }
|
68
|
+
}
|
69
|
+
}
|
70
|
+
end
|
71
|
+
|
72
|
+
def build_policy(queue_url, topic_arn)
|
73
|
+
queue_arn = Klunk::Queue.get_attributes(queue_url)['QueueArn']
|
74
|
+
{
|
75
|
+
'Version': '2012-10-17',
|
76
|
+
'Id': "#{queue_arn}/SQSDefaultPolicy",
|
77
|
+
'Statement': [
|
78
|
+
build_statement(queue_url, topic_arn)
|
79
|
+
]
|
80
|
+
}
|
81
|
+
end
|
82
|
+
|
83
|
+
def client
|
84
|
+
@client ||= Aws::SNS::Client.new
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
data/lib/klunk.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'klunk/version'
|
2
|
+
require 'klunk/configuration'
|
3
|
+
require 'klunk/queue'
|
4
|
+
require 'klunk/topic'
|
5
|
+
|
6
|
+
module Klunk
|
7
|
+
class << self
|
8
|
+
attr_accessor :configuration, :configured
|
9
|
+
|
10
|
+
def configure
|
11
|
+
self.configured = true
|
12
|
+
self.configuration ||= Configuration.new
|
13
|
+
yield(configuration)
|
14
|
+
end
|
15
|
+
|
16
|
+
def configured?
|
17
|
+
configured
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
require 'klunk/railtie' if defined?(Rails)
|
Binary file
|
metadata
ADDED
@@ -0,0 +1,149 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: klunk
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Marco Antonio Gonzalez Junior
|
8
|
+
- Wagner Vaz
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2016-12-18 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: aws-sdk
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
requirements:
|
18
|
+
- - "~>"
|
19
|
+
- !ruby/object:Gem::Version
|
20
|
+
version: '2'
|
21
|
+
type: :runtime
|
22
|
+
prerelease: false
|
23
|
+
version_requirements: !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - "~>"
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
version: '2'
|
28
|
+
- !ruby/object:Gem::Dependency
|
29
|
+
name: safe_yaml
|
30
|
+
requirement: !ruby/object:Gem::Requirement
|
31
|
+
requirements:
|
32
|
+
- - ">="
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: '0'
|
35
|
+
type: :runtime
|
36
|
+
prerelease: false
|
37
|
+
version_requirements: !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
39
|
+
- - ">="
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
version: '0'
|
42
|
+
- !ruby/object:Gem::Dependency
|
43
|
+
name: json
|
44
|
+
requirement: !ruby/object:Gem::Requirement
|
45
|
+
requirements:
|
46
|
+
- - ">="
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: '0'
|
49
|
+
type: :runtime
|
50
|
+
prerelease: false
|
51
|
+
version_requirements: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - ">="
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: '0'
|
56
|
+
- !ruby/object:Gem::Dependency
|
57
|
+
name: bundler
|
58
|
+
requirement: !ruby/object:Gem::Requirement
|
59
|
+
requirements:
|
60
|
+
- - "~>"
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: '1.13'
|
63
|
+
type: :development
|
64
|
+
prerelease: false
|
65
|
+
version_requirements: !ruby/object:Gem::Requirement
|
66
|
+
requirements:
|
67
|
+
- - "~>"
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '1.13'
|
70
|
+
- !ruby/object:Gem::Dependency
|
71
|
+
name: rake
|
72
|
+
requirement: !ruby/object:Gem::Requirement
|
73
|
+
requirements:
|
74
|
+
- - "~>"
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: '10.0'
|
77
|
+
type: :development
|
78
|
+
prerelease: false
|
79
|
+
version_requirements: !ruby/object:Gem::Requirement
|
80
|
+
requirements:
|
81
|
+
- - "~>"
|
82
|
+
- !ruby/object:Gem::Version
|
83
|
+
version: '10.0'
|
84
|
+
- !ruby/object:Gem::Dependency
|
85
|
+
name: rspec
|
86
|
+
requirement: !ruby/object:Gem::Requirement
|
87
|
+
requirements:
|
88
|
+
- - "~>"
|
89
|
+
- !ruby/object:Gem::Version
|
90
|
+
version: '3.0'
|
91
|
+
type: :development
|
92
|
+
prerelease: false
|
93
|
+
version_requirements: !ruby/object:Gem::Requirement
|
94
|
+
requirements:
|
95
|
+
- - "~>"
|
96
|
+
- !ruby/object:Gem::Version
|
97
|
+
version: '3.0'
|
98
|
+
description: Enterprise Messaging System on Rails
|
99
|
+
email:
|
100
|
+
- kayaman.baurets@gmail.com
|
101
|
+
- wv@0xd.co
|
102
|
+
executables: []
|
103
|
+
extensions: []
|
104
|
+
extra_rdoc_files: []
|
105
|
+
files:
|
106
|
+
- ".gitignore"
|
107
|
+
- ".rspec"
|
108
|
+
- ".rubocop.yml"
|
109
|
+
- Gemfile
|
110
|
+
- README.md
|
111
|
+
- Rakefile
|
112
|
+
- klunk.gemspec
|
113
|
+
- lib/generators/klunk/USAGE
|
114
|
+
- lib/generators/klunk/install_generator.rb
|
115
|
+
- lib/generators/klunk/templates/config/initializers/klunk.rb
|
116
|
+
- lib/generators/klunk/templates/config/queues.yml
|
117
|
+
- lib/generators/klunk/templates/config/topics.yml
|
118
|
+
- lib/klunk.rb
|
119
|
+
- lib/klunk/configuration.rb
|
120
|
+
- lib/klunk/queue.rb
|
121
|
+
- lib/klunk/railtie.rb
|
122
|
+
- lib/klunk/tasks/klunk.rake
|
123
|
+
- lib/klunk/topic.rb
|
124
|
+
- lib/klunk/version.rb
|
125
|
+
- vulture_squadron.jpg
|
126
|
+
homepage: https://github.com/kayaman/klunk
|
127
|
+
licenses: []
|
128
|
+
metadata: {}
|
129
|
+
post_install_message:
|
130
|
+
rdoc_options: []
|
131
|
+
require_paths:
|
132
|
+
- lib
|
133
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
134
|
+
requirements:
|
135
|
+
- - ">="
|
136
|
+
- !ruby/object:Gem::Version
|
137
|
+
version: '0'
|
138
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
139
|
+
requirements:
|
140
|
+
- - ">="
|
141
|
+
- !ruby/object:Gem::Version
|
142
|
+
version: '0'
|
143
|
+
requirements: []
|
144
|
+
rubyforge_project:
|
145
|
+
rubygems_version: 2.6.8
|
146
|
+
signing_key:
|
147
|
+
specification_version: 4
|
148
|
+
summary: Enterprise Messaging System on Rails
|
149
|
+
test_files: []
|