sync_client 0.0.14 → 0.1.7

Sign up to get free protection for your applications and to get access to all the features.
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