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 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
- Within the model you want to publish attributes to a service include something like the following:
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
- A priority is used for publishing to ensure eventual delivery if the message queue does not respond. Supported priority queues include Delayed Job and Resque.
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
- def create
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
- def update
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
- def destroy
72
- game = Game.find(self.id)
73
- game.each do |ga|
74
- ga.destroy
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 PubMessage < Message
2
+ class PubMessage < SyncClient::Message
3
3
 
4
4
  def publish
5
5
  SyncClient.background_task_queue.enqueue(Jobs::SyncClientJobs::Publish, self)
@@ -22,6 +22,10 @@ module SyncClient
22
22
  def destroy
23
23
  raise NotImplementedError
24
24
  end
25
+
26
+ def sync
27
+ raise NotImplementedError
28
+ end
25
29
  end
26
30
  end
27
31
  end
@@ -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("Recieved Message: #{object_type}##{action}")
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: 'asdufasdf8a7sd8fa7sdf', project_id: 'project_id'
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.add_message_object_handler object_name, handler_class, actions
12
- # config.add_message_object_handler 'StatNgin::Game', 'Resource::StatNgin::Game', [:update, :create, :destroy]
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
@@ -1,6 +1,7 @@
1
1
  require 'sync_client/configurators/message_handlers'
2
2
  require 'sync_client/task_queue/delayed_job'
3
3
  require 'sync_client/task_queue/resque'
4
+ require 'sync_client/task_queue/sidekiq'
4
5
  require 'sync_client/task_queue/inline_task_queue'
5
6
 
6
7
  module SyncClient
@@ -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
- before_update :publish_update
7
- after_destroy :publish_destroy
8
- after_create :publish_create
9
- end
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
- self.attributes
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] || CALLBACK_DEFAULTS
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' || attributes.any?{|attr| object.send("#{attr}_changed?")}
22
+ action.to_s != 'update' || object.any_attributes_changed?(attributes)
23
23
  end
24
24
 
25
25
  def resolve_condition(object)
@@ -0,0 +1,7 @@
1
+ module SyncClient
2
+ class Sidekiq
3
+ def self.enqueue(job, message)
4
+ ::Sidekiq::Client.enqueue(job, message)
5
+ end
6
+ end
7
+ end
@@ -1,3 +1,3 @@
1
1
  module SyncClient
2
- VERSION = "0.0.14"
2
+ VERSION = "0.1.7"
3
3
  end
@@ -0,0 +1,11 @@
1
+ require 'mongoid'
2
+
3
+ class Stat
4
+ include ::Mongoid::Document
5
+ include SyncClient::Publisher
6
+
7
+ publish_changes_of :values, :to => :test
8
+
9
+ field :pid, :as => :player_id
10
+ field :values, :type => Hash
11
+ end
@@ -0,0 +1,6 @@
1
+ class GamePublisher
2
+ include SyncClient::Publisher
3
+
4
+ attr_reader :game_id, :result
5
+ attr_accessor :score
6
+ end
@@ -2,13 +2,13 @@ SyncClient.config do |config|
2
2
 
3
3
  config.queuel do |c|
4
4
  c.default_queue 'Dummy'
5
- # c.credentials token: 'asdufasdf8a7sd8fa7sdf', project_id: 'project_id'
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.add_message_object_handler object_name, handler_class, actions
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