sync_client 0.0.14 → 0.1.7
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 +15 -0
- data/README.md +91 -22
- data/app/models/sync_client/pub_message.rb +1 -1
- data/app/models/sync_client/service_resource/base.rb +4 -0
- data/app/models/sync_client/sub_message.rb +2 -2
- data/lib/generators/templates/sync_client.rb +3 -3
- data/lib/sync_client/configurator.rb +1 -0
- data/lib/sync_client/publisher/active_record_publisher.rb +20 -0
- data/lib/sync_client/publisher/base_publisher.rb +39 -0
- data/lib/sync_client/publisher/mongoid_publisher.rb +28 -0
- data/lib/sync_client/publisher/poro_publisher.rb +33 -0
- data/lib/sync_client/publisher.rb +9 -41
- data/lib/sync_client/queue_publisher.rb +2 -2
- data/lib/sync_client/sync_queue.rb +2 -2
- data/lib/sync_client/task_queue/sidekiq.rb +7 -0
- data/lib/sync_client/version.rb +1 -1
- data/test/dummy/app/models/stat.rb +11 -0
- data/test/dummy/app/services/game_publisher.rb +6 -0
- data/test/dummy/config/initializers/sync_client.rb +3 -3
- data/test/dummy/log/test.log +8048 -0
- data/test/lib/publisher/active_record_publisher_test.rb +82 -0
- data/test/lib/publisher/mongoid_publisher_test.rb +84 -0
- data/test/lib/publisher/poro_publisher_test.rb +84 -0
- data/test/lib/publisher_test.rb +14 -3
- data/test/lib/queue_publisher_test.rb +9 -0
- data/test/lib/sync_queue_test.rb +7 -0
- data/test/test_helper.rb +5 -3
- metadata +99 -84
checksums.yaml
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
---
|
2
|
+
!binary "U0hBMQ==":
|
3
|
+
metadata.gz: !binary |-
|
4
|
+
NWFkZjA0MTY4NTkyNDdjMWJiYTQ0ZThjNjg5NzVmNzI5MDYyZmIxZg==
|
5
|
+
data.tar.gz: !binary |-
|
6
|
+
YWNjM2EzNzJhY2M3NDE4MmI5NDNkODljMzE2NzRmMjQ2MjQ3ODMxYw==
|
7
|
+
SHA512:
|
8
|
+
metadata.gz: !binary |-
|
9
|
+
MjEzYjc0NjY1Yjc2M2VkMzkwNzUwYWNjNmI2MmNlNDZiNDJlMWQ3NmI2MDFh
|
10
|
+
NzQwMTJkZTUxN2ViMGE5NzQ3YzU2YmNkNmZkZWUzZDgwMDdjNzVlMjE0OGNm
|
11
|
+
ZjA4YzQ4MDJmZmY3MGUzMGNkNDQzYWM5MTY2YmU4Yjk0NmUwM2Q=
|
12
|
+
data.tar.gz: !binary |-
|
13
|
+
YjU3ZmNkMDc3MGE2YzAwZDNmZmQ0OTVjOTk5NjQ1MjBiYTRkZDk0MzcyNjM5
|
14
|
+
NDlkZDVmNWQ3NGQ5MjJlZmQyNTQyOGNlMDg3NDU1NWRkODFjYzE5MGQwNGM5
|
15
|
+
OThhNDhmYTliNDNiMmZmODE2YzAxMzU5ODYwN2Q5ZDc3NGNhYzg=
|
data/README.md
CHANGED
@@ -1,6 +1,9 @@
|
|
1
1
|
# SyncClient
|
2
|
+
[![Build Status][build_status_image]][build_status]
|
3
|
+
[![Coverage Status][coverage_status_image]][coverage_status]
|
4
|
+
|
5
|
+
This gem simplifies syncing data between services by using delayed job processing and a message queue for guaranteed delivery and eventual consistency. SyncClient defines messages based on a resource and action system to simplify message publishing and handling. It supports inline processing and delayed job processeding by Resque, DelayedJob, and Sidekiq. See [Queuel](https://rubygems.org/gems/sync_client) for a list of supported message queues.
|
2
6
|
|
3
|
-
This gem simplifies syncing data between services by using a resque queue and a message queue for guaranteed delivery and eventual consistency of data.
|
4
7
|
|
5
8
|
## Installation
|
6
9
|
|
@@ -24,12 +27,35 @@ $ rails g sync_client:install
|
|
24
27
|
|
25
28
|
Edit configuation in `config/initializers/sync_client.rb`
|
26
29
|
|
30
|
+
## Configuration
|
31
|
+
|
32
|
+
SyncClient requires that message queue creditials, a background task queue, and message handler definitions to be defined in the configuration file. Note that any message that does not match a defined handler is simply dropped from the queue.
|
33
|
+
|
34
|
+
```ruby
|
35
|
+
SyncClient.config do |config|
|
36
|
+
|
37
|
+
config.queuel do |c|
|
38
|
+
# c.default_queue 'VenueService'
|
39
|
+
# c.credentials token: [token], project_id: 'project_id'
|
40
|
+
# c.engine :iron_mq
|
41
|
+
end
|
42
|
+
# config.background_task_queue SyncClient::Resque #or SyncClient::DelayedJob or write your own.
|
43
|
+
# config.queue_suffix 'suffix'
|
44
|
+
# config.logger Logger.new
|
45
|
+
# config.add_message_handler object_name, handler_class, actions
|
46
|
+
# config.add_message_handler 'Service::Game', 'Service::Game', [:update, :create, :destroy]
|
47
|
+
end
|
48
|
+
```
|
49
|
+
|
50
|
+
|
27
51
|
## Usage
|
28
52
|
|
29
|
-
|
53
|
+
### Publisher
|
54
|
+
|
55
|
+
SyncClient provides a simple interface to define what attributes to publish to a service for associated actions where the message class is the class of the resource. For models that inherit from `ActiveRecord::Base` or include `Mongoid::Document`, add the `SyncClient::Publisher` module and configure like the following:
|
30
56
|
|
31
57
|
```ruby
|
32
|
-
class Team
|
58
|
+
class Team < ActiveRecord::Base
|
33
59
|
include SyncClient::Publisher
|
34
60
|
publish_changes_of :name, :color, to: :queue, for: [:update, :destroy], if: lambda{|team| !team.name.nil?}
|
35
61
|
# options:
|
@@ -39,8 +65,34 @@ class Team
|
|
39
65
|
end
|
40
66
|
```
|
41
67
|
|
68
|
+
If you are just working with a "Plain Old Ruby Object", you can do something
|
69
|
+
like this:
|
70
|
+
|
71
|
+
```ruby
|
72
|
+
class TeamSchedulerService
|
73
|
+
include SyncClient::Publisher
|
74
|
+
publish_to :queue
|
75
|
+
# options:
|
76
|
+
# for: callbacks for publishing (default: :sync)
|
77
|
+
# if/unless: condition for publishing
|
78
|
+
|
79
|
+
def do_some_stuffs
|
80
|
+
# CODEZ
|
81
|
+
sync
|
82
|
+
# return value
|
83
|
+
end
|
84
|
+
end
|
85
|
+
```
|
86
|
+
|
87
|
+
And then in a seperate method, you are able to call the `sync` method and fire
|
88
|
+
the queue syncing yourself.
|
89
|
+
|
90
|
+
The `sync` method is mapped to the `:sync` action in the ServiceResource by
|
91
|
+
default, but `:create`, `:update` and `:destroy` are still available for you to
|
92
|
+
use as well.
|
42
93
|
|
43
|
-
|
94
|
+
|
95
|
+
### Poller
|
44
96
|
|
45
97
|
Run the message queue poller:
|
46
98
|
|
@@ -51,30 +103,47 @@ $ bundle exec script/sync_client restart
|
|
51
103
|
$ bundle exec script/sync_client stop
|
52
104
|
```
|
53
105
|
|
54
|
-
Define handlers for the all messages such as the following:
|
55
|
-
|
56
|
-
```ruby
|
57
|
-
class Game < SyncClient::ServiceResource::Base
|
58
|
-
attr_accessor :id
|
59
|
-
attr_accessor :starts_at
|
60
|
-
attr_accessor :ends_at
|
61
106
|
|
62
|
-
|
63
|
-
Game.create(:game_id => self.id, :starts_at => self.starts_at, :ends_at => self.ends_at)
|
64
|
-
end
|
107
|
+
### Service Resource
|
65
108
|
|
66
|
-
|
67
|
-
game = Game.find(self.id).first
|
68
|
-
game.update_attributes(:starts_at => self.starts_at, :ends_at => self.ends_at)
|
69
|
-
end
|
109
|
+
Message handlers for the actions are then defined matching the handler class set in the configuration file where each method corresponds to the message action as follows:
|
70
110
|
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
111
|
+
```ruby
|
112
|
+
module Service
|
113
|
+
class Game < SyncClient::ServiceResource::Base
|
114
|
+
|
115
|
+
# Determine what attributes are to be accessible using attr_accessors
|
116
|
+
attr_accessor :id
|
117
|
+
attr_accessor :starts_at
|
118
|
+
attr_accessor :ends_at
|
119
|
+
|
120
|
+
def create
|
121
|
+
Game.create(:game_id => self.id, :starts_at => self.starts_at, :ends_at => self.ends_at)
|
122
|
+
end
|
123
|
+
|
124
|
+
def update
|
125
|
+
game = Game.find(self.id).first
|
126
|
+
game.update_attributes(:starts_at => self.starts_at, :ends_at => self. ends_at)
|
127
|
+
end
|
128
|
+
|
129
|
+
def destroy
|
130
|
+
game = Game.find(self.id)
|
131
|
+
game.destroy
|
75
132
|
end
|
76
133
|
end
|
77
134
|
end
|
78
135
|
```
|
79
136
|
|
137
|
+
## Contributing
|
138
|
+
|
139
|
+
1. Fork it ( https://github.com/[my-github-username]/sync_client/fork )
|
140
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
141
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
142
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
143
|
+
5. Create a new Pull Request
|
144
|
+
|
145
|
+
[build_status]: https://travis-ci.org/sportngin/sync_client
|
146
|
+
[build_status_image]: https://travis-ci.org/sportngin/sync_client.svg?branch=master
|
147
|
+
[coverage_status]: https://coveralls.io/r/sportngin/sync_client
|
148
|
+
[coverage_status_image]: https://img.shields.io/coveralls/sportngin/sync_client.svg
|
80
149
|
|
@@ -1,5 +1,5 @@
|
|
1
1
|
module SyncClient
|
2
|
-
class SubMessage < Message
|
2
|
+
class SubMessage < SyncClient::Message
|
3
3
|
attr_accessor :success
|
4
4
|
|
5
5
|
def process
|
@@ -29,7 +29,7 @@ module SyncClient
|
|
29
29
|
|
30
30
|
def with_logging(&block)
|
31
31
|
SyncClient.logger.info("------------------------------------------")
|
32
|
-
SyncClient.logger.info("
|
32
|
+
SyncClient.logger.info("Received Message: #{object_type}##{action}")
|
33
33
|
yield
|
34
34
|
SyncClient.logger.info("Error Occured: #{error}") if error
|
35
35
|
SyncClient.logger.info("Processed Message: #{!!success}")
|
@@ -2,12 +2,12 @@ SyncClient.config do |config|
|
|
2
2
|
|
3
3
|
config.queuel do |c|
|
4
4
|
# c.default_queue 'VenueService'
|
5
|
-
# c.credentials token:
|
5
|
+
# c.credentials token: [token], project_id: 'project_id'
|
6
6
|
# c.engine :iron_mq
|
7
7
|
end
|
8
8
|
# config.background_task_queue SyncClient::Resque #or SyncClient::DelayedJob or write your own.
|
9
9
|
# config.queue_suffix 'suffix'
|
10
10
|
# config.logger Logger.new
|
11
|
-
# config.
|
12
|
-
# config.
|
11
|
+
# config.add_message_handler object_name, handler_class, actions
|
12
|
+
# config.add_message_handler 'StatNgin::Game', 'Resource::StatNgin::Game', [:update, :create, :destroy]
|
13
13
|
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'sync_client/publisher/base_publisher'
|
2
|
+
|
3
|
+
module SyncClient
|
4
|
+
module Publisher
|
5
|
+
module ActiveRecordPublisher
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
included do
|
8
|
+
include SyncClient::Publisher::BasePublisher
|
9
|
+
end
|
10
|
+
|
11
|
+
def any_attributes_changed?(attributes)
|
12
|
+
attributes.any?{|attr| send("#{attr}_changed?")}
|
13
|
+
end
|
14
|
+
|
15
|
+
def publisher_attributes
|
16
|
+
self.attributes
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module SyncClient
|
2
|
+
module Publisher
|
3
|
+
module BasePublisher
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
included do
|
7
|
+
after_create :publish_create
|
8
|
+
before_update :publish_update
|
9
|
+
after_destroy :publish_destroy
|
10
|
+
end
|
11
|
+
|
12
|
+
module ClassMethods
|
13
|
+
def publish_changes_of(*attributes, options)
|
14
|
+
@queue_publisher = SyncClient::QueuePublisher.new unless @queue_publisher
|
15
|
+
@queue_publisher.add_publisher(attributes, options)
|
16
|
+
end
|
17
|
+
|
18
|
+
attr_reader :queue_publisher
|
19
|
+
end
|
20
|
+
|
21
|
+
def queue_publisher
|
22
|
+
self.class.queue_publisher
|
23
|
+
end
|
24
|
+
|
25
|
+
def publish_create(options = {})
|
26
|
+
queue_publisher.publish(:create, self, options)
|
27
|
+
end
|
28
|
+
|
29
|
+
def publish_update(options = {})
|
30
|
+
queue_publisher.publish(:update, self, options)
|
31
|
+
end
|
32
|
+
|
33
|
+
def publish_destroy(options = {})
|
34
|
+
queue_publisher.publish(:destroy, self, options)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'sync_client/publisher/base_publisher'
|
2
|
+
|
3
|
+
module SyncClient
|
4
|
+
module Publisher
|
5
|
+
module MongoidPublisher
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
|
8
|
+
included do
|
9
|
+
include SyncClient::Publisher::BasePublisher
|
10
|
+
end
|
11
|
+
|
12
|
+
def any_attributes_changed?(attributes)
|
13
|
+
attributes.any?{|attr| send("#{attr}_changed?")}
|
14
|
+
end
|
15
|
+
|
16
|
+
def publisher_attributes
|
17
|
+
self.attributes.inject({}) { |attrs, (raw_key, raw_value)|
|
18
|
+
if raw_key =='_id'
|
19
|
+
attrs['id'] = raw_value
|
20
|
+
else
|
21
|
+
attrs[self.aliased_fields.invert.fetch(raw_key) { raw_key }] = raw_value
|
22
|
+
end
|
23
|
+
attrs
|
24
|
+
}
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module SyncClient
|
2
|
+
module Publisher
|
3
|
+
module PoroPublisher
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
module ClassMethods
|
7
|
+
def publish_to(*args)
|
8
|
+
@queue_publisher ||= SyncClient::QueuePublisher.new
|
9
|
+
options = args.last.is_a?(Hash) ? args.pop : {:for => :sync}
|
10
|
+
args.each do |end_point|
|
11
|
+
@queue_publisher.add_publisher([], options.merge(:to => end_point))
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
attr_reader :queue_publisher
|
16
|
+
end
|
17
|
+
|
18
|
+
def queue_publisher
|
19
|
+
self.class.queue_publisher
|
20
|
+
end
|
21
|
+
|
22
|
+
def sync(action=:sync)
|
23
|
+
queue_publisher.publish(action, self)
|
24
|
+
end
|
25
|
+
|
26
|
+
def publisher_attributes
|
27
|
+
instance_variable_names.inject({}) do |result, var|
|
28
|
+
result.merge Hash[var.gsub('@', ''), instance_variable_get(var)]
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -1,50 +1,18 @@
|
|
1
|
+
require 'sync_client/publisher/active_record_publisher'
|
2
|
+
require 'sync_client/publisher/mongoid_publisher'
|
3
|
+
require 'sync_client/publisher/poro_publisher'
|
4
|
+
|
1
5
|
module SyncClient
|
2
6
|
module Publisher
|
3
7
|
extend ActiveSupport::Concern
|
4
8
|
|
5
9
|
included do
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
module ClassMethods
|
12
|
-
def publish_changes_of(*attributes, options)
|
13
|
-
@queue_publisher = SyncClient::QueuePublisher.new unless @queue_publisher
|
14
|
-
@queue_publisher.add_publisher(attributes, options)
|
15
|
-
end
|
16
|
-
|
17
|
-
attr_reader :queue_publisher
|
18
|
-
end
|
19
|
-
|
20
|
-
def queue_publisher
|
21
|
-
self.class.queue_publisher
|
22
|
-
end
|
23
|
-
|
24
|
-
def publish_update
|
25
|
-
queue_publisher.publish(:update, self)
|
26
|
-
end
|
27
|
-
|
28
|
-
def publish_destroy
|
29
|
-
queue_publisher.publish(:destroy, self)
|
30
|
-
end
|
31
|
-
|
32
|
-
def publish_create
|
33
|
-
queue_publisher.publish(:create, self)
|
34
|
-
end
|
35
|
-
|
36
|
-
def publisher_attributes
|
37
|
-
if self.respond_to?(:aliased_fields)
|
38
|
-
self.attributes.inject({}) { |attrs, (raw_key, raw_value)|
|
39
|
-
if raw_key =='_id'
|
40
|
-
attrs['id'] = raw_value
|
41
|
-
else
|
42
|
-
attrs[self.aliased_fields.invert.fetch(raw_key) { raw_key }] = raw_value
|
43
|
-
end
|
44
|
-
attrs
|
45
|
-
}
|
10
|
+
if defined?(::ActiveRecord::Base) && self <= ::ActiveRecord::Base
|
11
|
+
include SyncClient::Publisher::ActiveRecordPublisher
|
12
|
+
elsif defined?(::Mongoid::Document) && self.included_modules.include?(::Mongoid::Document)
|
13
|
+
include SyncClient::Publisher::MongoidPublisher
|
46
14
|
else
|
47
|
-
|
15
|
+
include SyncClient::Publisher::PoroPublisher
|
48
16
|
end
|
49
17
|
end
|
50
18
|
end
|
@@ -10,10 +10,10 @@ module SyncClient
|
|
10
10
|
self.sync_queues << SyncClient::SyncQueue.new(attributes, options)
|
11
11
|
end
|
12
12
|
|
13
|
-
def publish(action, object)
|
13
|
+
def publish(action, object, options = {})
|
14
14
|
sync_queues.each do |sync_queue|
|
15
15
|
SyncClient.logger.info("#{sync_queue.inspect}")
|
16
|
-
queue_message(action, object, sync_queue.queue).publish if sync_queue.publishable?(action, object)
|
16
|
+
queue_message(action, object, sync_queue.queue).publish if options[:force] || sync_queue.publishable?(action, object)
|
17
17
|
end
|
18
18
|
end
|
19
19
|
|
@@ -10,7 +10,7 @@ module SyncClient
|
|
10
10
|
def initialize(attributes, options)
|
11
11
|
@attributes = attributes
|
12
12
|
@queue = options[:to]
|
13
|
-
@callbacks = options[:for]
|
13
|
+
@callbacks = options[:for] ? Array(options[:for]) : CALLBACK_DEFAULTS
|
14
14
|
@options = options
|
15
15
|
end
|
16
16
|
|
@@ -19,7 +19,7 @@ module SyncClient
|
|
19
19
|
end
|
20
20
|
|
21
21
|
def update?(action, object)
|
22
|
-
action.to_s != 'update' ||
|
22
|
+
action.to_s != 'update' || object.any_attributes_changed?(attributes)
|
23
23
|
end
|
24
24
|
|
25
25
|
def resolve_condition(object)
|
data/lib/sync_client/version.rb
CHANGED
@@ -2,13 +2,13 @@ SyncClient.config do |config|
|
|
2
2
|
|
3
3
|
config.queuel do |c|
|
4
4
|
c.default_queue 'Dummy'
|
5
|
-
|
5
|
+
c.credentials token: '', project_id: ''
|
6
6
|
# c.engine :iron_mq
|
7
7
|
end
|
8
8
|
config.background_task_queue SyncClient::InlineTaskQueue
|
9
9
|
config.queue_suffix ''
|
10
10
|
config.logger Logger.new(STDOUT)
|
11
|
-
|
12
|
-
# config.
|
11
|
+
|
12
|
+
# config.add_message_handler object_name, handler_class, actions
|
13
13
|
config.add_message_handler 'Dummy::Game', 'Game', [:update, :create, :destroy]
|
14
14
|
end
|