trabox 0.1.1
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/MIT-LICENSE +20 -0
- data/README.md +35 -0
- data/Rakefile +3 -0
- data/exe/trabox +21 -0
- data/lib/generators/trabox/configure/USAGE +8 -0
- data/lib/generators/trabox/configure/configure_generator.rb +11 -0
- data/lib/generators/trabox/configure/templates/initializer.rb +44 -0
- data/lib/generators/trabox/model/USAGE +8 -0
- data/lib/generators/trabox/model/model_generator.rb +31 -0
- data/lib/tasks/trabox_tasks.rake +4 -0
- data/lib/trabox/command/parser.rb +40 -0
- data/lib/trabox/command.rb +19 -0
- data/lib/trabox/commands/common/configuration.rb +14 -0
- data/lib/trabox/commands/common/runner.rb +18 -0
- data/lib/trabox/commands/relay/argument_parser.rb +52 -0
- data/lib/trabox/commands/relay/configuration.rb +63 -0
- data/lib/trabox/commands/relay.rb +51 -0
- data/lib/trabox/commands/subscribe/argument_parser.rb +49 -0
- data/lib/trabox/commands/subscribe/configuration.rb +38 -0
- data/lib/trabox/commands/subscribe.rb +39 -0
- data/lib/trabox/metric.rb +145 -0
- data/lib/trabox/publisher/google/cloud_pubsub.rb +89 -0
- data/lib/trabox/publisher.rb +8 -0
- data/lib/trabox/railtie.rb +4 -0
- data/lib/trabox/relay/relayable.rb +27 -0
- data/lib/trabox/relay/relayable_models.rb +34 -0
- data/lib/trabox/relay/relayer.rb +62 -0
- data/lib/trabox/relay.rb +9 -0
- data/lib/trabox/subscriber/google/cloud_pubsub.rb +63 -0
- data/lib/trabox/subscriber.rb +7 -0
- data/lib/trabox/version.rb +3 -0
- data/lib/trabox.rb +8 -0
- metadata +146 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 44bcd5a8b174aa887c7cdbd415aeeba5f1af25a11e10a8a23543967ba2f4fd84
|
4
|
+
data.tar.gz: 0b2d81b7a826f457dd24763bf5eb39278d14061293cf75f225b54639e13057e4
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 02b16217802b7beaa2c2f87e6bf60681556554c458f839c34ed7cfc049c80107be7a5646b2ae2d42de76159e4973776701cb4020987d9d2462dcad75e4183f14
|
7
|
+
data.tar.gz: 3b45467e241f3339de38f6502121347bc569d3b0ac9f29ff286befbbcd9d4866dd348c6a650e4fa63d901c6ba0eb3c8bfe90244bd7b812568c498366c82d4dcb
|
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright 2022 TODO: Write your name
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
# Trabox
|
2
|
+
|
3
|
+
Transactional-Outbox for Rails.
|
4
|
+
|
5
|
+
## Usage
|
6
|
+
|
7
|
+
How to use my plugin.
|
8
|
+
|
9
|
+
## Installation
|
10
|
+
|
11
|
+
Add this line to your application's Gemfile:
|
12
|
+
|
13
|
+
```ruby
|
14
|
+
gem 'trabox'
|
15
|
+
```
|
16
|
+
|
17
|
+
And then execute:
|
18
|
+
|
19
|
+
```bash
|
20
|
+
bundle
|
21
|
+
```
|
22
|
+
|
23
|
+
Or install it yourself as:
|
24
|
+
|
25
|
+
```bash
|
26
|
+
gem install trabox
|
27
|
+
```
|
28
|
+
|
29
|
+
## Contributing
|
30
|
+
|
31
|
+
Contribution directions go here.
|
32
|
+
|
33
|
+
## License
|
34
|
+
|
35
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
data/exe/trabox
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'trabox/command'
|
4
|
+
|
5
|
+
$stdout.sync = true
|
6
|
+
$stderr.sync = true
|
7
|
+
|
8
|
+
begin
|
9
|
+
command_parser = Trabox::Command::Parser.new
|
10
|
+
command = command_parser.parse!
|
11
|
+
|
12
|
+
Trabox::Command.invoke command
|
13
|
+
rescue StandardError => e
|
14
|
+
warn e.backtrace
|
15
|
+
warn e.message
|
16
|
+
warn ''
|
17
|
+
warn command_parser.usage
|
18
|
+
exit 1
|
19
|
+
end
|
20
|
+
|
21
|
+
# vim:ft=ruby:
|
@@ -0,0 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Trabox
|
4
|
+
class ConfigureGenerator < Rails::Generators::Base
|
5
|
+
source_root File.expand_path('templates', __dir__)
|
6
|
+
|
7
|
+
def create_initializer
|
8
|
+
copy_file 'initializer.rb', 'config/initializers/trabox.rb'
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# Trabox::Metric.setup unless Rails.env.test?
|
2
|
+
|
3
|
+
Trabox::Command::Relay.configure do |config|
|
4
|
+
config.limit = 10
|
5
|
+
|
6
|
+
# ref: https://api.rubyonrails.org/classes/ActiveRecord/Locking/Pessimistic.html#method-i-lock-21
|
7
|
+
config.lock = true
|
8
|
+
config.interval = 5
|
9
|
+
config.log_level = :info
|
10
|
+
|
11
|
+
# String or Proc
|
12
|
+
ordering_key = Trabox::Publisher::Google::Cloud::PubSub::OrderingKey.new ->(event) { event.model_name.name }
|
13
|
+
|
14
|
+
config.publisher = Trabox::Publisher::Google::Cloud::PubSub.new(
|
15
|
+
'trabox',
|
16
|
+
message_ordering: true,
|
17
|
+
ordering_key: ordering_key
|
18
|
+
)
|
19
|
+
end
|
20
|
+
|
21
|
+
Trabox::Command::Subscribe.configure do |config|
|
22
|
+
config.log_level = :info
|
23
|
+
|
24
|
+
before_listen_acknowledge_callbacks = []
|
25
|
+
before_listen_acknowledge_callbacks << lambda do |received_message|
|
26
|
+
Rails.logger.info "id=#{received_message.message_id} message=#{received_message.data} ordering_key=#{received_message.ordering_key}"
|
27
|
+
end
|
28
|
+
|
29
|
+
# after_listen_acknowledge_callbacks = []
|
30
|
+
# after_listen_acknowledge_callbacks << lambda do |_received_message|
|
31
|
+
# end
|
32
|
+
|
33
|
+
# error_listen_callbacks = []
|
34
|
+
# error_listen_callbacks << lambda do |_exception|
|
35
|
+
# end
|
36
|
+
|
37
|
+
config.subscriber = Trabox::Subscriber::Google::Cloud::PubSub.new(
|
38
|
+
'trabox-sub',
|
39
|
+
listen_options: {},
|
40
|
+
before_listen_acknowledge_callbacks: before_listen_acknowledge_callbacks,
|
41
|
+
after_listen_acknowledge_callbacks: [],
|
42
|
+
error_listen_callbacks: []
|
43
|
+
)
|
44
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'rails/generators/active_record/model/model_generator'
|
2
|
+
|
3
|
+
module Trabox
|
4
|
+
class ModelGenerator < ActiveRecord::Generators::ModelGenerator
|
5
|
+
source_root "#{base_root}/active_record/model/templates"
|
6
|
+
|
7
|
+
class_option :polymorphic, type: :string, desc: 'add polymorphic column.'
|
8
|
+
|
9
|
+
def initialize(*args)
|
10
|
+
super
|
11
|
+
|
12
|
+
add_attribute "#{options[:polymorphic]}:references{polymorphic}" if options[:polymorphic].present?
|
13
|
+
|
14
|
+
add_attribute 'event_data:binary'
|
15
|
+
add_attribute 'message_id:string'
|
16
|
+
add_attribute 'published_at:datetime'
|
17
|
+
end
|
18
|
+
|
19
|
+
def insert_include
|
20
|
+
inject_into_class File.join('app/models', class_path, "#{file_name}.rb"), class_name do
|
21
|
+
" include Trabox::Relay::Relayable\n"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def add_attribute(attribute)
|
28
|
+
attributes << Rails::Generators::GeneratedAttribute.parse(attribute)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'optparse'
|
2
|
+
|
3
|
+
module Trabox
|
4
|
+
module Command
|
5
|
+
class Parser
|
6
|
+
def initialize
|
7
|
+
@parser = OptionParser.new(nil, 20) do |o|
|
8
|
+
o.version = VERSION
|
9
|
+
o.banner = "\e[1mUsage: #{o.program_name}\e[0m <COMMAND> [OPTIONS]"
|
10
|
+
o.on_tail('-h', '--help', 'Print help information') do
|
11
|
+
$stdout.puts usage
|
12
|
+
exit 0
|
13
|
+
end
|
14
|
+
|
15
|
+
o.on_tail('-v', '--version', 'Print version information')
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def parse!
|
20
|
+
@parser.order!
|
21
|
+
|
22
|
+
command = ARGV.shift
|
23
|
+
|
24
|
+
return command unless command.nil?
|
25
|
+
|
26
|
+
warn usage
|
27
|
+
exit 1
|
28
|
+
end
|
29
|
+
|
30
|
+
def usage
|
31
|
+
<<~USAGE
|
32
|
+
#{@parser.help}
|
33
|
+
\e[1mCommands\e[0m:
|
34
|
+
r, relay Relay events
|
35
|
+
s, subscribe Subscribe events
|
36
|
+
USAGE
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'trabox/command/parser'
|
2
|
+
require 'trabox/commands/relay'
|
3
|
+
require 'trabox/commands/subscribe'
|
4
|
+
|
5
|
+
module Trabox
|
6
|
+
module Command
|
7
|
+
# @param command [String]
|
8
|
+
def self.invoke(command)
|
9
|
+
case command
|
10
|
+
when 'r', 'relay'
|
11
|
+
Trabox::Command::Relay.perform
|
12
|
+
when 's', 'subscribe'
|
13
|
+
Trabox::Command::Subscribe.perform
|
14
|
+
else
|
15
|
+
raise "no such command: #{command}"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module Trabox
|
2
|
+
module Command
|
3
|
+
class Configuration
|
4
|
+
DEFAULT_LOG_LEVEL = :info
|
5
|
+
# @!attribute [rw] log_level
|
6
|
+
# @return [Symbol]
|
7
|
+
attr_accessor :log_level
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
@log_level = ENV['TRABOX_LOG_LEVEL']&.downcase&.to_sym || DEFAULT_LOG_LEVEL
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Trabox
|
4
|
+
module Command
|
5
|
+
module Runner
|
6
|
+
RAILS_ROOT = ENV['RAILS_ROOT'] || '.'
|
7
|
+
APP_PATH ||= "#{RAILS_ROOT}/config/application"
|
8
|
+
|
9
|
+
def self.load_runner
|
10
|
+
require "#{RAILS_ROOT}/config/boot"
|
11
|
+
|
12
|
+
require APP_PATH
|
13
|
+
Rails.application.require_environment!
|
14
|
+
Rails.application.load_runner
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'optparse'
|
2
|
+
require_relative './configuration'
|
3
|
+
|
4
|
+
module Trabox
|
5
|
+
module Command
|
6
|
+
module Relay
|
7
|
+
class ArgumentParser
|
8
|
+
def self.parse!
|
9
|
+
new
|
10
|
+
end
|
11
|
+
|
12
|
+
def initialize
|
13
|
+
opts = parse!
|
14
|
+
|
15
|
+
config_overwrite(opts)
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def parse!
|
21
|
+
opts = {}
|
22
|
+
|
23
|
+
@parser = OptionParser.new do |o|
|
24
|
+
o.banner = <<~BANNER
|
25
|
+
\e[1mUsage\e[0m: \e[1mtrabox relay\e[0m [OPTIONS]
|
26
|
+
|
27
|
+
Overwrite configuration
|
28
|
+
|
29
|
+
BANNER
|
30
|
+
|
31
|
+
o.on('-l NUM', '--limit', Integer)
|
32
|
+
o.on('-i SEC', '--interval', Integer)
|
33
|
+
o.on('-L', '--[no-]lock', TrueClass)
|
34
|
+
o.on('--log-level LEVEL', String) { |v| v.downcase.to_sym }
|
35
|
+
end
|
36
|
+
|
37
|
+
@parser.parse!(into: opts)
|
38
|
+
|
39
|
+
opts.transform_keys { |k| k.to_s.underscore.to_sym }
|
40
|
+
end
|
41
|
+
|
42
|
+
def config_overwrite(opts)
|
43
|
+
opts.each do |attr, val|
|
44
|
+
next if val.nil?
|
45
|
+
|
46
|
+
Relay.config.send("#{attr}=", val)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
require_relative '../common/configuration'
|
2
|
+
|
3
|
+
module Trabox
|
4
|
+
module Command
|
5
|
+
module Relay
|
6
|
+
class << self
|
7
|
+
attr_accessor :active
|
8
|
+
alias active? active
|
9
|
+
|
10
|
+
def configure
|
11
|
+
return unless active?
|
12
|
+
|
13
|
+
yield config
|
14
|
+
end
|
15
|
+
|
16
|
+
def config
|
17
|
+
@config ||= Configuration.new
|
18
|
+
end
|
19
|
+
|
20
|
+
def config_activate
|
21
|
+
@active = true
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
class Configuration < Command::Configuration
|
26
|
+
DEFAULT_SELECT_LIMIT = 3
|
27
|
+
DEFAULT_INTERVAL = 5
|
28
|
+
DEFAULT_LOCK = true
|
29
|
+
|
30
|
+
# @!attribute [rw] limit
|
31
|
+
# @return [Integer]
|
32
|
+
# @!attribute [rw] interval
|
33
|
+
# @return [Integer]
|
34
|
+
# @!attribute [rw] lock
|
35
|
+
# @return [Boolean, String]
|
36
|
+
# @!attribute [rw] publisher
|
37
|
+
# @return [Trabox::Publisher, Class]
|
38
|
+
attr_accessor :limit,
|
39
|
+
:interval,
|
40
|
+
:lock,
|
41
|
+
:publisher
|
42
|
+
|
43
|
+
def initialize
|
44
|
+
@limit = ENV['TRABOX_RELAYER_LIMIT'] || DEFAULT_SELECT_LIMIT
|
45
|
+
@interval = ENV['TRABOX_RELAYER_INTERVAL'] || DEFAULT_INTERVAL
|
46
|
+
@lock = ENV['TRABOX_RELAYER_LOCK'] || DEFAULT_LOCK
|
47
|
+
|
48
|
+
super
|
49
|
+
end
|
50
|
+
|
51
|
+
def interval=(interval)
|
52
|
+
@interval = interval.to_i
|
53
|
+
end
|
54
|
+
|
55
|
+
def check
|
56
|
+
return if @publisher.respond_to?(:publish)
|
57
|
+
|
58
|
+
raise 'Relay Configuration: config.publisher must be have :publish method.'
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require_relative './relay/argument_parser'
|
2
|
+
require_relative './relay/configuration'
|
3
|
+
require_relative './common/runner'
|
4
|
+
|
5
|
+
module Trabox
|
6
|
+
module Command
|
7
|
+
module Relay
|
8
|
+
class << self
|
9
|
+
def prepare
|
10
|
+
config_activate
|
11
|
+
|
12
|
+
Runner.load_runner
|
13
|
+
|
14
|
+
ArgumentParser.parse!
|
15
|
+
|
16
|
+
config.check
|
17
|
+
|
18
|
+
Rails.logger.level = config.log_level
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.perform
|
23
|
+
prepare
|
24
|
+
|
25
|
+
relayer = Trabox::Relay::Relayer.new(
|
26
|
+
config.publisher,
|
27
|
+
limit: config.limit,
|
28
|
+
lock: config.lock
|
29
|
+
)
|
30
|
+
|
31
|
+
interval = config.interval
|
32
|
+
|
33
|
+
loop do
|
34
|
+
begin
|
35
|
+
relayer.perform
|
36
|
+
|
37
|
+
Metric.service_check('relay.service.check', Metric::SERVICE_OK)
|
38
|
+
rescue StandardError => e
|
39
|
+
Rails.logger.error e
|
40
|
+
|
41
|
+
ActiveRecord::Base.clear_all_connections!
|
42
|
+
|
43
|
+
Metric.service_check('relay.service.check', Metric::SERVICE_CRITICAL)
|
44
|
+
end
|
45
|
+
|
46
|
+
sleep interval
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'optparse'
|
2
|
+
require_relative './configuration'
|
3
|
+
|
4
|
+
module Trabox
|
5
|
+
module Command
|
6
|
+
module Subscribe
|
7
|
+
class ArgumentParser
|
8
|
+
def self.parse!
|
9
|
+
new
|
10
|
+
end
|
11
|
+
|
12
|
+
def initialize
|
13
|
+
opts = parse!
|
14
|
+
|
15
|
+
config_overwrite(opts)
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def parse!
|
21
|
+
opts = {}
|
22
|
+
|
23
|
+
@parser = OptionParser.new do |o|
|
24
|
+
o.banner = <<~BANNER
|
25
|
+
\e[1mUsage\e[0m: \e[1mtrabox subscribe\e[0m [OPTIONS]
|
26
|
+
|
27
|
+
Overwrite configuration
|
28
|
+
|
29
|
+
BANNER
|
30
|
+
|
31
|
+
o.on('--log-level LEVEL', String) { |v| v.downcase.to_sym }
|
32
|
+
end
|
33
|
+
|
34
|
+
@parser.parse!(into: opts)
|
35
|
+
|
36
|
+
opts.transform_keys { |k| k.to_s.underscore.to_sym }
|
37
|
+
end
|
38
|
+
|
39
|
+
def config_overwrite(opts)
|
40
|
+
opts.each do |attr, val|
|
41
|
+
next if val.nil?
|
42
|
+
|
43
|
+
Subscribe.config.send("#{attr}=", val)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require_relative '../common/configuration'
|
2
|
+
|
3
|
+
module Trabox
|
4
|
+
module Command
|
5
|
+
module Subscribe
|
6
|
+
class << self
|
7
|
+
attr_accessor :active
|
8
|
+
alias active? active
|
9
|
+
|
10
|
+
def configure
|
11
|
+
return unless active?
|
12
|
+
|
13
|
+
yield config
|
14
|
+
end
|
15
|
+
|
16
|
+
def config
|
17
|
+
@config ||= Configuration.new
|
18
|
+
end
|
19
|
+
|
20
|
+
def config_activate
|
21
|
+
@active = true
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
class Configuration < Command::Configuration
|
26
|
+
# @!attribute [rw] subscriber
|
27
|
+
# @return [Trabox::Subscriber, Class]
|
28
|
+
attr_accessor :subscriber
|
29
|
+
|
30
|
+
def check
|
31
|
+
return if @subscriber.respond_to?(:subscribe)
|
32
|
+
|
33
|
+
raise 'Subscribe Configuration: config.subscriber must be have :subscribe method.'
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require_relative './subscribe/argument_parser'
|
2
|
+
require_relative './subscribe/configuration'
|
3
|
+
require_relative './common/runner'
|
4
|
+
|
5
|
+
module Trabox
|
6
|
+
module Command
|
7
|
+
module Subscribe
|
8
|
+
class << self
|
9
|
+
def prepare
|
10
|
+
config_activate
|
11
|
+
|
12
|
+
Runner.load_runner
|
13
|
+
|
14
|
+
ArgumentParser.parse!
|
15
|
+
|
16
|
+
config.check
|
17
|
+
|
18
|
+
Rails.logger.level = config.log_level
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.perform
|
23
|
+
prepare
|
24
|
+
|
25
|
+
subscriber = config.subscriber
|
26
|
+
|
27
|
+
Metric.service_check('subscribe.service.check', Metric::SERVICE_OK)
|
28
|
+
|
29
|
+
begin
|
30
|
+
subscriber.subscribe
|
31
|
+
rescue StandardError => e
|
32
|
+
Rails.logger.error e
|
33
|
+
|
34
|
+
Metric.service_check('subscribe.service.check', Metric::SERVICE_CRITICAL)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,145 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'datadog/statsd'
|
4
|
+
|
5
|
+
module Trabox
|
6
|
+
# Traboxのメトリクス
|
7
|
+
#
|
8
|
+
# - unpublished_event_count: パブリッシュするイベント数
|
9
|
+
# - published_event_count: パブリッシュしたイベント数
|
10
|
+
# - find_events_error_count: パブリッシュするイベントの取得に失敗した数
|
11
|
+
# - publish_event_error_count: イベントのパブリッシュに失敗した数
|
12
|
+
# - update_event_record_error_count: パブリッシュしたイベントのカラム更新に失敗した数
|
13
|
+
module Metric
|
14
|
+
NAMESPACE = ENV.fetch('METRIC_NAMESPACE', 'trabox')
|
15
|
+
LOG_PREFIX = '[metric]'
|
16
|
+
SERVICE_OK = Datadog::Statsd::OK
|
17
|
+
SERVICE_CRITICAL = Datadog::Statsd::CRITICAL
|
18
|
+
|
19
|
+
class << self
|
20
|
+
attr_reader :statsd
|
21
|
+
|
22
|
+
# Datadog::Statsd.new arguments
|
23
|
+
def setup(*args, **kwargs)
|
24
|
+
@statsd = Datadog::Statsd.new(*args, **kwargs)
|
25
|
+
end
|
26
|
+
|
27
|
+
def service_check(name, status, opts = {})
|
28
|
+
name = metric_name(name)
|
29
|
+
|
30
|
+
@statsd&.service_check(name, status, opts)
|
31
|
+
|
32
|
+
status = case status
|
33
|
+
when SERVICE_OK
|
34
|
+
'ok'
|
35
|
+
when SERVICE_CRITICAL
|
36
|
+
'critical'
|
37
|
+
else
|
38
|
+
'undefined'
|
39
|
+
end
|
40
|
+
|
41
|
+
Rails.logger.debug "#{LOG_PREFIX} type=service_check name=#{name} status=#{status} opts=#{opts}"
|
42
|
+
end
|
43
|
+
|
44
|
+
def count(name, count, opts = {})
|
45
|
+
name = metric_name(name)
|
46
|
+
|
47
|
+
@statsd&.count(name, count, opts)
|
48
|
+
|
49
|
+
Rails.logger.debug "#{LOG_PREFIX} type=count name=#{name} count=#{count} opts=#{opts}"
|
50
|
+
end
|
51
|
+
|
52
|
+
def increment(name, opts = {})
|
53
|
+
count name, 1, opts
|
54
|
+
end
|
55
|
+
|
56
|
+
def decrement(name, opts = {})
|
57
|
+
count name, -1, opts
|
58
|
+
end
|
59
|
+
|
60
|
+
def distribution(name, value, opts = {})
|
61
|
+
name = metric_name(name)
|
62
|
+
|
63
|
+
@statsd&.distribution name, value, opts
|
64
|
+
|
65
|
+
Rails.logger.debug "#{LOG_PREFIX} type=distribution name=#{name} value=#{value} opts=#{opts}"
|
66
|
+
end
|
67
|
+
|
68
|
+
def distribution_time(name, opts = {}, &block)
|
69
|
+
name = metric_name(name)
|
70
|
+
|
71
|
+
@statsd&.distribution_time name, opts, &block
|
72
|
+
|
73
|
+
Rails.logger.debug "#{LOG_PREFIX} type=distribution_time name=#{name} opts=#{opts}"
|
74
|
+
end
|
75
|
+
|
76
|
+
def event(title, text, opts = {})
|
77
|
+
@statsd&.event title, text, opts
|
78
|
+
|
79
|
+
Rails.logger.debug "#{LOG_PREFIX} type=event title=#{title} opts=#{opts}"
|
80
|
+
end
|
81
|
+
|
82
|
+
def gauge(name, value, opts = {})
|
83
|
+
name = metric_name(name)
|
84
|
+
|
85
|
+
@statsd&.gauge name, value, opts
|
86
|
+
|
87
|
+
Rails.logger.debug "#{LOG_PREFIX} type=gauge name=#{name} value=#{value} opts=#{opts}"
|
88
|
+
end
|
89
|
+
|
90
|
+
def histogram(name, value, opts = {})
|
91
|
+
name = metric_name(name)
|
92
|
+
|
93
|
+
@statsd&.histogram name, value, opts
|
94
|
+
|
95
|
+
Rails.logger.debug "#{LOG_PREFIX} type=histogram name=#{name} value=#{value} opts=#{opts}"
|
96
|
+
end
|
97
|
+
|
98
|
+
def set(name, value, opts = {})
|
99
|
+
name = metric_name(name)
|
100
|
+
|
101
|
+
@statsd&.set name, value, opts
|
102
|
+
|
103
|
+
Rails.logger.debug "#{LOG_PREFIX} type=set name=#{name} value=#{value} opts=#{opts}"
|
104
|
+
end
|
105
|
+
|
106
|
+
def time(name, opts = {}, &block)
|
107
|
+
name = metric_name(name)
|
108
|
+
|
109
|
+
@statsd&.time name, opts, &block
|
110
|
+
|
111
|
+
Rails.logger.debug "#{LOG_PREFIX} type=time name=#{name} opts=#{opts}"
|
112
|
+
end
|
113
|
+
|
114
|
+
def timing(name, ms, opts = {})
|
115
|
+
name = metric_name(name)
|
116
|
+
|
117
|
+
@statsd&.timing name, ms, opts
|
118
|
+
|
119
|
+
Rails.logger.debug "#{LOG_PREFIX} type=timing name=#{name} ms=#{ms} opts=#{opts}"
|
120
|
+
end
|
121
|
+
|
122
|
+
def batch
|
123
|
+
yield self
|
124
|
+
end
|
125
|
+
|
126
|
+
def close(**kwargs)
|
127
|
+
@statsd&.close(**kwargs)
|
128
|
+
|
129
|
+
Rails.logger.debug "#{LOG_PREFIX} type=close opts=#{kwargs}"
|
130
|
+
end
|
131
|
+
|
132
|
+
def flush(**kwargs)
|
133
|
+
@statsd&.flush(**kwargs)
|
134
|
+
|
135
|
+
Rails.logger.debug "#{LOG_PREFIX} type=flush opts=#{kwargs}"
|
136
|
+
end
|
137
|
+
|
138
|
+
private
|
139
|
+
|
140
|
+
def metric_name(name)
|
141
|
+
"#{NAMESPACE}.#{name}"
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
require 'google/cloud/pubsub'
|
2
|
+
|
3
|
+
# Google::Cloud::PubSub::Topicのラッパー
|
4
|
+
module Trabox
|
5
|
+
module Publisher
|
6
|
+
module Google
|
7
|
+
module Cloud
|
8
|
+
class PubSub
|
9
|
+
include Trabox::Publisher
|
10
|
+
|
11
|
+
LOG_PREFIX = '[google pubsub]'.freeze
|
12
|
+
|
13
|
+
class OrderingKey
|
14
|
+
# @param key [Proc(ActiveRecord), String]
|
15
|
+
def initialize(key)
|
16
|
+
@key = if key.is_a?(Proc)
|
17
|
+
key
|
18
|
+
elsif key.instance_of?(String)
|
19
|
+
->(*) { key }
|
20
|
+
else
|
21
|
+
raise ArgumentError, 'OrderingKey.new should be set to Proc or String.'
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
# @return [String]
|
26
|
+
def call(*args)
|
27
|
+
key = @key.call(*args)
|
28
|
+
|
29
|
+
raise 'OrderingKey#call should be returned String type.' unless key.instance_of?(String)
|
30
|
+
|
31
|
+
key
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
# @param topic_id [String]
|
36
|
+
# @param message_ordering [Boolean] enable_message_ordering
|
37
|
+
# @param ordering_key [OrderingKey]
|
38
|
+
def initialize(topic_id, message_ordering: true, ordering_key: nil)
|
39
|
+
raise ArgumentError, 'topic_id must be specified.' if topic_id.blank?
|
40
|
+
|
41
|
+
unless ordering_key.nil? || ordering_key.is_a?(OrderingKey)
|
42
|
+
raise ArgumentError,
|
43
|
+
'ordering_key must be specified OrderingKey class or nil.'
|
44
|
+
end
|
45
|
+
|
46
|
+
# @type [Google::Cloud::PubSub::Project]
|
47
|
+
@pubsub = ::Google::Cloud::PubSub.new
|
48
|
+
|
49
|
+
# @type [Google::Cloud::PubSub::Topic]
|
50
|
+
@topic = @pubsub.topic topic_id
|
51
|
+
|
52
|
+
@ordering_key = ordering_key
|
53
|
+
|
54
|
+
raise "Topic-ID='#{topic_id}' does not exist." if @topic.nil?
|
55
|
+
|
56
|
+
@topic.enable_message_ordering! if message_ordering
|
57
|
+
rescue StandardError => e
|
58
|
+
Rails.logger.error "#{LOG_PREFIX} #{e.message}"
|
59
|
+
raise
|
60
|
+
end
|
61
|
+
|
62
|
+
# @param event [ActiveRecord] publishするイベント
|
63
|
+
# @return message_id [String]
|
64
|
+
def publish(event)
|
65
|
+
raise ArgumentError, 'event should be set to trabox:model' unless event.respond_to?(:event_data)
|
66
|
+
|
67
|
+
message = event.event_data
|
68
|
+
|
69
|
+
raise ArgumentError, 'published message must not be blank' if message.blank?
|
70
|
+
|
71
|
+
published_message = if @ordering_key
|
72
|
+
@topic.publish message, ordering_key: @ordering_key.call(event)
|
73
|
+
else
|
74
|
+
@topic.publish message
|
75
|
+
end
|
76
|
+
|
77
|
+
Rails.logger.debug "#{LOG_PREFIX} message published. " \
|
78
|
+
"message_id=#{published_message.message_id} ordering_key=#{published_message.ordering_key}"
|
79
|
+
|
80
|
+
published_message.message_id
|
81
|
+
rescue StandardError => e
|
82
|
+
Rails.logger.error "#{LOG_PREFIX} #{e.message}"
|
83
|
+
raise
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# 未publishなレコードやpublish完了時のレコード操作を追加するためのモジュール
|
2
|
+
module Trabox
|
3
|
+
module Relay
|
4
|
+
module Relayable
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
included do
|
8
|
+
# @param limit [Integer]
|
9
|
+
scope :unpublished, lambda { |limit: DEFAULT_SELECT_LIMIT|
|
10
|
+
where(published_at: nil).limit(limit).order(created_at: :asc)
|
11
|
+
}
|
12
|
+
end
|
13
|
+
|
14
|
+
# message_idとpublished_atを更新する
|
15
|
+
# publishした結果からpublishした時間を取得できないため、Timeクラスを使用する
|
16
|
+
#
|
17
|
+
# @param message_id [String]
|
18
|
+
def published_done!(message_id)
|
19
|
+
raise ArgumentError if message_id.blank?
|
20
|
+
|
21
|
+
update!(message_id: message_id, published_at: Time.current.to_formatted_s(:iso8601))
|
22
|
+
|
23
|
+
Rails.logger.debug "Event record updated. message_id=#{message_id}"
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# Relayableモジュールをincludeしているモデル一覧を取得するモジュール
|
2
|
+
module Trabox
|
3
|
+
module Relay
|
4
|
+
module RelayableModels
|
5
|
+
# @return [Array<Class>]
|
6
|
+
def self.list
|
7
|
+
if @models.nil?
|
8
|
+
load_models
|
9
|
+
|
10
|
+
@models = ApplicationRecord.descendants.filter do |klass|
|
11
|
+
klass.ancestors.include?(Relayable)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
Rails.logger.debug "Relayed event models: #{@models.map { |model| model.name.underscore }}"
|
16
|
+
|
17
|
+
@models
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.load_models
|
21
|
+
return if Rails.application.config.eager_load
|
22
|
+
|
23
|
+
# test, developmentモードは遅延読み込みのためmodelsをロードする
|
24
|
+
Rails.logger.debug 'Load models'
|
25
|
+
|
26
|
+
Dir["#{Rails.root}/app/models/**/*.rb"].each do |file|
|
27
|
+
require_dependency file
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
private_class_method :load_models
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
module Trabox
|
2
|
+
module Relay
|
3
|
+
class Relayer
|
4
|
+
# @param publisher [Trabox::PubSub::Publisher]
|
5
|
+
# @param limit [Integer] SELECT文のLIMIT
|
6
|
+
# @param lock [Boolean, String] ActiveRecord lock argument
|
7
|
+
def initialize(publisher, limit: DEFAULT_SELECT_LIMIT, lock: true)
|
8
|
+
raise ArgumentError unless publisher.respond_to?(:publish)
|
9
|
+
|
10
|
+
@publisher = publisher
|
11
|
+
@limit = limit
|
12
|
+
@lock = lock
|
13
|
+
end
|
14
|
+
|
15
|
+
def perform
|
16
|
+
RelayableModels.list.each do |model|
|
17
|
+
model.transaction do
|
18
|
+
unpublished_events = begin
|
19
|
+
model.lock(@lock).unpublished limit: @limit
|
20
|
+
rescue StandardError
|
21
|
+
Metric.increment('find_events_error_count',
|
22
|
+
tags: ["event-type:#{model.name.underscore}"])
|
23
|
+
raise
|
24
|
+
end
|
25
|
+
|
26
|
+
unpublished_events.each do |event|
|
27
|
+
Metric.increment('unpublished_event_count',
|
28
|
+
tags: ["event-type:#{event.class.name.underscore}", "event-id:#{event.id}"])
|
29
|
+
|
30
|
+
publish_and_commit(event)
|
31
|
+
|
32
|
+
Metric.increment('published_event_count',
|
33
|
+
tags: ["event-type:#{event.class.name.underscore}", "event-id:#{event.id}"])
|
34
|
+
end
|
35
|
+
|
36
|
+
Rails.logger.info "Published events. (#{model.name.underscore}=#{unpublished_events.size})"
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
def publish_and_commit(event)
|
44
|
+
begin
|
45
|
+
message_id = @publisher.publish event
|
46
|
+
rescue StandardError
|
47
|
+
Metric.increment('published_event_error_count',
|
48
|
+
tags: ["event-type:#{event.class.name.underscore}", "event-id:#{event.id}"])
|
49
|
+
raise
|
50
|
+
end
|
51
|
+
|
52
|
+
begin
|
53
|
+
event.published_done! message_id
|
54
|
+
rescue StandardError
|
55
|
+
Metric.increment('update_event_record_error_count',
|
56
|
+
tags: ["event-type:#{event.class.name.underscore}", "event-id:#{event.id}"])
|
57
|
+
raise
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
data/lib/trabox/relay.rb
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
module Trabox
|
2
|
+
module Subscriber
|
3
|
+
module Google
|
4
|
+
module Cloud
|
5
|
+
class PubSub
|
6
|
+
include Subscriber
|
7
|
+
|
8
|
+
# @param subscription_id [String]
|
9
|
+
# @param listen_options [Hash] listen method options
|
10
|
+
# @param before_listen_acknowledge_callbacks [Array<Proc>]
|
11
|
+
# @param after_listen_acknowledge_callbacks [Array<Proc>]
|
12
|
+
# @param error_listen_callbacks [Array<Proc>]
|
13
|
+
def initialize(subscription_id,
|
14
|
+
listen_options: {},
|
15
|
+
before_listen_acknowledge_callbacks: [],
|
16
|
+
after_listen_acknowledge_callbacks: [],
|
17
|
+
error_listen_callbacks: [])
|
18
|
+
|
19
|
+
@listen_options = listen_options
|
20
|
+
@before_listen_acknowledge_callbacks = before_listen_acknowledge_callbacks
|
21
|
+
@after_listen_acknowledge_callbacks = after_listen_acknowledge_callbacks
|
22
|
+
@error_listen_callbacks = error_listen_callbacks
|
23
|
+
|
24
|
+
@pubsub = ::Google::Cloud::PubSub.new
|
25
|
+
|
26
|
+
@subscription = @pubsub.subscription subscription_id
|
27
|
+
|
28
|
+
raise "Subscription-ID='#{subscription_id}' does not exist." if @subscription.nil?
|
29
|
+
|
30
|
+
Rails.logger.info "Subscription '#{subscription_id}': message ordering is #{@subscription.message_ordering?}."
|
31
|
+
end
|
32
|
+
|
33
|
+
def subscribe
|
34
|
+
subscriber = @subscription.listen(**@listen_options) do |received_message|
|
35
|
+
@before_listen_acknowledge_callbacks.each do |cb|
|
36
|
+
cb.call(received_message)
|
37
|
+
end
|
38
|
+
|
39
|
+
received_message.acknowledge!
|
40
|
+
|
41
|
+
@after_listen_acknowledge_callbacks.each do |cb|
|
42
|
+
cb.call(received_message)
|
43
|
+
end
|
44
|
+
|
45
|
+
Metric.service_check('subscribe.service.check', Metric::SERVICE_OK)
|
46
|
+
end
|
47
|
+
|
48
|
+
subscriber.on_error do |_|
|
49
|
+
Metric.service_check('subscribe.service.check', Metric::SERVICE_CRITICAL)
|
50
|
+
end
|
51
|
+
|
52
|
+
@error_listen_callbacks.each do |cb|
|
53
|
+
subscriber.on_error(&cb)
|
54
|
+
end
|
55
|
+
|
56
|
+
Rails.logger.info 'Listening subscrition...'
|
57
|
+
subscriber.start.wait!
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
data/lib/trabox.rb
ADDED
metadata
ADDED
@@ -0,0 +1,146 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: trabox
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- kosay
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2022-12-06 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: dogstatsd-ruby
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: google-cloud-pubsub
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: mysql2
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: optparse
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rails
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
description: Transactional-Outbox for Rails
|
84
|
+
email:
|
85
|
+
- ekr59uv25@gmail.com
|
86
|
+
executables:
|
87
|
+
- trabox
|
88
|
+
extensions: []
|
89
|
+
extra_rdoc_files: []
|
90
|
+
files:
|
91
|
+
- MIT-LICENSE
|
92
|
+
- README.md
|
93
|
+
- Rakefile
|
94
|
+
- exe/trabox
|
95
|
+
- lib/generators/trabox/configure/USAGE
|
96
|
+
- lib/generators/trabox/configure/configure_generator.rb
|
97
|
+
- lib/generators/trabox/configure/templates/initializer.rb
|
98
|
+
- lib/generators/trabox/model/USAGE
|
99
|
+
- lib/generators/trabox/model/model_generator.rb
|
100
|
+
- lib/tasks/trabox_tasks.rake
|
101
|
+
- lib/trabox.rb
|
102
|
+
- lib/trabox/command.rb
|
103
|
+
- lib/trabox/command/parser.rb
|
104
|
+
- lib/trabox/commands/common/configuration.rb
|
105
|
+
- lib/trabox/commands/common/runner.rb
|
106
|
+
- lib/trabox/commands/relay.rb
|
107
|
+
- lib/trabox/commands/relay/argument_parser.rb
|
108
|
+
- lib/trabox/commands/relay/configuration.rb
|
109
|
+
- lib/trabox/commands/subscribe.rb
|
110
|
+
- lib/trabox/commands/subscribe/argument_parser.rb
|
111
|
+
- lib/trabox/commands/subscribe/configuration.rb
|
112
|
+
- lib/trabox/metric.rb
|
113
|
+
- lib/trabox/publisher.rb
|
114
|
+
- lib/trabox/publisher/google/cloud_pubsub.rb
|
115
|
+
- lib/trabox/railtie.rb
|
116
|
+
- lib/trabox/relay.rb
|
117
|
+
- lib/trabox/relay/relayable.rb
|
118
|
+
- lib/trabox/relay/relayable_models.rb
|
119
|
+
- lib/trabox/relay/relayer.rb
|
120
|
+
- lib/trabox/subscriber.rb
|
121
|
+
- lib/trabox/subscriber/google/cloud_pubsub.rb
|
122
|
+
- lib/trabox/version.rb
|
123
|
+
homepage: https://github.com/sarub0b0/trabox
|
124
|
+
licenses:
|
125
|
+
- MIT
|
126
|
+
metadata: {}
|
127
|
+
post_install_message:
|
128
|
+
rdoc_options: []
|
129
|
+
require_paths:
|
130
|
+
- lib
|
131
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
132
|
+
requirements:
|
133
|
+
- - ">="
|
134
|
+
- !ruby/object:Gem::Version
|
135
|
+
version: '0'
|
136
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
137
|
+
requirements:
|
138
|
+
- - ">="
|
139
|
+
- !ruby/object:Gem::Version
|
140
|
+
version: '0'
|
141
|
+
requirements: []
|
142
|
+
rubygems_version: 3.3.7
|
143
|
+
signing_key:
|
144
|
+
specification_version: 4
|
145
|
+
summary: Transactional-Outbox for Rails
|
146
|
+
test_files: []
|