syncromesh 0.1.0 → 0.3.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 +4 -4
- data/lib/reactive_record/base.rb +20 -0
- data/lib/reactive_record/sync_wrapper.rb +18 -0
- data/lib/reactive_record/syncromesh.rb.erb +79 -0
- data/lib/syncromesh.rb +174 -3
- data/lib/syncromesh/configuration.rb +29 -0
- data/lib/syncromesh/version.rb +1 -1
- data/spec/spec_helper.rb +2 -0
- data/spec/syncromesh_spec.rb +11 -0
- data/syncromesh.gemspec +16 -27
- metadata +16 -48
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0d803e6d6129ea7733ea11227a18b3e589d80fc9
|
4
|
+
data.tar.gz: 2ce570a9ff38672c527436b13cbf6b52d5ddaec3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 503a5620019e3ad138b1d75dd072816eb4b994e70315260e1070b519ffdf305a686f97945488c830d161e3e81ca5bfc5f2e16928d38e10461419d004037b01ad
|
7
|
+
data.tar.gz: 6b16087b2b02d0e13d6ebb3ccaaf7a6931a21e93bde2420e47aa2075db69187261533ed9695d606654128f99f0a582d17f11391c430886972be2585f73c25871
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module ReactiveRecord
|
2
|
+
|
3
|
+
class Base
|
4
|
+
|
5
|
+
def self.when_not_saving(model)
|
6
|
+
if @records[model].detect { |record| record.saving? }
|
7
|
+
poller = every (0.1) do
|
8
|
+
unless @records[model].detect { |record| record.saving? }
|
9
|
+
poller.stop
|
10
|
+
yield model
|
11
|
+
end
|
12
|
+
end
|
13
|
+
else
|
14
|
+
yield model
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
module ReactiveRecord
|
2
|
+
|
3
|
+
class Syncromesh
|
4
|
+
|
5
|
+
include React::IsomorphicHelpers
|
6
|
+
|
7
|
+
def self.sync_change(data)
|
8
|
+
Base.when_not_saving(Object.const_get(data[:klass])) do |klass|
|
9
|
+
klass._react_param_conversion(data[:record]).backing_record.sync_scopes
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.sync_destroy(data)
|
14
|
+
record = Object.const_get(data[:klass])._react_param_conversion(data[:record])
|
15
|
+
ReactiveRecord::Base.load_data { record.destroy }
|
16
|
+
record.backing_record.destroyed = true
|
17
|
+
record.backing_record.sync_scopes
|
18
|
+
end
|
19
|
+
|
20
|
+
before_first_mount do |context|
|
21
|
+
if on_opal_client?
|
22
|
+
|
23
|
+
<% if Syncromesh.transport == :pusher %>
|
24
|
+
|
25
|
+
change = lambda do |data|
|
26
|
+
sync_change Hash.new(data)
|
27
|
+
end
|
28
|
+
|
29
|
+
destroy = lambda do |data|
|
30
|
+
sync_destroy Hash.new(data)
|
31
|
+
end
|
32
|
+
|
33
|
+
%x{
|
34
|
+
<% if Syncromesh.client_logging %>
|
35
|
+
Pusher.log = function(message) {
|
36
|
+
if (window.console && window.console.log) {
|
37
|
+
window.console.log(message);
|
38
|
+
}
|
39
|
+
};
|
40
|
+
<% end %>
|
41
|
+
|
42
|
+
var pusher = new Pusher('<%= Syncromesh.key %>', {
|
43
|
+
encrypted: <%= Syncromesh.encrypted %>
|
44
|
+
});
|
45
|
+
|
46
|
+
var channel = pusher.subscribe('<%= Syncromesh.channel %>');
|
47
|
+
channel.bind('change', change);
|
48
|
+
channel.bind('destroy', destroy);
|
49
|
+
}
|
50
|
+
|
51
|
+
<% elsif Syncromesh.transport == :simple_poller %>
|
52
|
+
|
53
|
+
id = nil
|
54
|
+
|
55
|
+
HTTP.get(`window.ReactiveRecordEnginePath`+"/syncromesh-subscribe/").then do |response|
|
56
|
+
id = response.json[:id]
|
57
|
+
end
|
58
|
+
|
59
|
+
puts "SECONDS BETWEEN POLL SET TO <%= Syncromesh.seconds_between_poll %> SECONDS"
|
60
|
+
|
61
|
+
every(<%= Syncromesh.seconds_between_poll %>) do
|
62
|
+
HTTP.get(`window.ReactiveRecordEnginePath`+"/syncromesh-read/#{id}").then do |response|
|
63
|
+
response.json.each do |update|
|
64
|
+
case update[0]
|
65
|
+
when :change
|
66
|
+
sync_change update[1]
|
67
|
+
when :destroy
|
68
|
+
sync_destroy update[1]
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
<% end %>
|
75
|
+
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
data/lib/syncromesh.rb
CHANGED
@@ -1,5 +1,176 @@
|
|
1
|
-
|
1
|
+
module ActiveRecord
|
2
|
+
class Base
|
3
|
+
|
4
|
+
class << self
|
5
|
+
|
6
|
+
def no_auto_sync
|
7
|
+
@no_auto_sync = true
|
8
|
+
end
|
9
|
+
|
10
|
+
alias_method :old_scope, :scope
|
11
|
+
|
12
|
+
def scope(name, server, client = nil)
|
13
|
+
if server == :no_sync
|
14
|
+
server = client
|
15
|
+
client = nil
|
16
|
+
elsif client.nil? && @no_auto_sync.nil?
|
17
|
+
client = server
|
18
|
+
end
|
19
|
+
if RUBY_ENGINE == 'opal' && client
|
20
|
+
to_sync name do |scope, model|
|
21
|
+
if ReactiveRecord::SyncWrapper.new(model).instance_eval(&client)
|
22
|
+
scope << model
|
23
|
+
else
|
24
|
+
scope.delete(model)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
old_scope(name, server)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
if RUBY_ENGINE == 'opal'
|
35
|
+
|
36
|
+
require_relative 'syncromesh/version'
|
37
|
+
require_relative 'reactive_record/syncromesh'
|
38
|
+
require_relative 'reactive_record/base'
|
39
|
+
require_relative 'reactive_record/sync_wrapper'
|
40
|
+
|
41
|
+
else
|
42
|
+
|
43
|
+
require 'opal'
|
44
|
+
require "syncromesh/version"
|
45
|
+
require "syncromesh/configuration"
|
46
|
+
|
47
|
+
module Syncromesh
|
48
|
+
|
49
|
+
extend Syncromesh::Configuration
|
50
|
+
|
51
|
+
define_setting :transport, :pusher
|
52
|
+
define_setting :app_id
|
53
|
+
define_setting :key
|
54
|
+
define_setting :secret
|
55
|
+
define_setting :encrypted, true
|
56
|
+
define_setting :channel_prefix
|
57
|
+
define_setting :seconds_polled_data_will_be_retained, 5*60
|
58
|
+
define_setting :seconds_between_poll, 0.50
|
59
|
+
define_setting :client_logging, true
|
60
|
+
|
61
|
+
def self.pusher
|
62
|
+
unless channel_prefix
|
63
|
+
transport = nil
|
64
|
+
raise "******** NO CHANNEL PREFIX SET ***************"
|
65
|
+
end
|
66
|
+
@pusher ||= Pusher::Client.new(
|
67
|
+
app_id: app_id,
|
68
|
+
key: key,
|
69
|
+
secret: secret,
|
70
|
+
encrypted: encrypted
|
71
|
+
)
|
72
|
+
end
|
73
|
+
|
74
|
+
def self.channel
|
75
|
+
"#{channel_prefix}"
|
76
|
+
end
|
77
|
+
|
78
|
+
def self.after_change(model)
|
79
|
+
if transport == :pusher
|
80
|
+
pusher.trigger(Syncromesh.channel, 'change', klass: model.class.name, record: model.react_serializer)
|
81
|
+
elsif transport == :simple_poller
|
82
|
+
SimplePoller.write('change', {klass: model.class.name, record: model.react_serializer})
|
83
|
+
elsif transport != :none
|
84
|
+
raise "Unknown transport #{Syncromesh.transport} - not supported"
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def self.after_destroy(model)
|
89
|
+
if transport == :pusher
|
90
|
+
pusher.trigger(Syncromesh.channel, 'destroy', klass: model.class.name, record: model.react_serializer)
|
91
|
+
elsif transport == :simple_poller
|
92
|
+
SimplePoller.write('destroy', {klass: model.class.name, record: model.react_serializer})
|
93
|
+
elsif transport != :none
|
94
|
+
raise "Unknown transport #{Syncromesh.transport} - not supported"
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
module SimplePoller
|
99
|
+
|
100
|
+
require "pstore"
|
101
|
+
|
102
|
+
def self.subscribe
|
103
|
+
subscriber = SecureRandom.hex(10)
|
104
|
+
update_store do |store|
|
105
|
+
store[subscriber] = {data: [], last_read_at: Time.now}
|
106
|
+
end
|
107
|
+
subscriber
|
108
|
+
end
|
109
|
+
|
110
|
+
def self.read(subscriber)
|
111
|
+
update_store do |store|
|
112
|
+
data = store[subscriber][:data] rescue []
|
113
|
+
store[subscriber] = {data: [], last_read_at: Time.now}
|
114
|
+
data
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
def self.write(event, data)
|
119
|
+
update_store do |store|
|
120
|
+
store.each do |subscriber, subscriber_store|
|
121
|
+
subscriber_store[:data] << [event, data]
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
end
|
126
|
+
|
127
|
+
def self.update_store
|
128
|
+
store = PStore.new('syncromesh-simple-poller-store')
|
129
|
+
store.transaction do
|
130
|
+
data = store[:data] || {}
|
131
|
+
data.delete_if do |subscriber, subscriber_store|
|
132
|
+
subscriber_store[:last_read_at] < Time.now-Syncromesh.seconds_polled_data_will_be_retained
|
133
|
+
end
|
134
|
+
result = yield data
|
135
|
+
store[:data] = data
|
136
|
+
result
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
module ActiveRecord
|
143
|
+
|
144
|
+
class Base
|
145
|
+
after_commit :syncromesh_after_change, on: [:create, :update]
|
146
|
+
after_commit :syncromesh_after_destroy, on: [:destroy]
|
147
|
+
|
148
|
+
def syncromesh_after_change
|
149
|
+
Syncromesh.after_change self
|
150
|
+
end
|
151
|
+
|
152
|
+
def syncromesh_after_destroy
|
153
|
+
Syncromesh.after_destroy self
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
end
|
158
|
+
|
159
|
+
module ReactiveRecord
|
160
|
+
|
161
|
+
class SyncromeshController < ::ActionController::Base
|
162
|
+
|
163
|
+
def subscribe
|
164
|
+
render json: {id: Syncromesh::SimplePoller.subscribe}
|
165
|
+
end
|
166
|
+
|
167
|
+
def read
|
168
|
+
render json: Syncromesh::SimplePoller.read(params[:subscriber])
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
end
|
173
|
+
|
174
|
+
Opal.append_path File.expand_path('../', __FILE__).untaint
|
2
175
|
|
3
|
-
module Syncromesh
|
4
|
-
# Your code goes here...
|
5
176
|
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Syncromesh
|
2
|
+
module Configuration
|
3
|
+
|
4
|
+
def configuration
|
5
|
+
yield self
|
6
|
+
end
|
7
|
+
|
8
|
+
def define_setting(name, default = nil)
|
9
|
+
class_variable_set("@@#{name}", default)
|
10
|
+
|
11
|
+
define_class_method "#{name}=" do |value|
|
12
|
+
class_variable_set("@@#{name}", value)
|
13
|
+
end
|
14
|
+
|
15
|
+
define_class_method name do
|
16
|
+
class_variable_get("@@#{name}")
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def define_class_method(name, &block)
|
23
|
+
(class << self; self; end).instance_eval do
|
24
|
+
define_method name, &block
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
end
|
data/lib/syncromesh/version.rb
CHANGED
data/spec/spec_helper.rb
ADDED
data/syncromesh.gemspec
CHANGED
@@ -1,33 +1,22 @@
|
|
1
|
-
#
|
2
|
-
|
3
|
-
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
-
require 'syncromesh/version'
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path('../lib/', __FILE__)
|
5
3
|
|
6
|
-
|
7
|
-
spec.name = "syncromesh"
|
8
|
-
spec.version = Syncromesh::VERSION
|
9
|
-
spec.authors = ["Mitch VanDuyn"]
|
10
|
-
spec.email = ["mitch@catprint.com"]
|
4
|
+
require 'syncromesh/version'
|
11
5
|
|
12
|
-
|
13
|
-
spec.description = "Work in progress"
|
14
|
-
spec.homepage = "https://github.com/reactive-ruby/syncromesh"
|
15
|
-
spec.license = "MIT"
|
6
|
+
Gem::Specification.new do |s|
|
16
7
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
# else
|
22
|
-
# raise "RubyGems 2.0 or newer is required to protect against public gem pushes."
|
23
|
-
# end
|
8
|
+
s.name = "syncromesh"
|
9
|
+
s.version = Syncromesh::VERSION
|
10
|
+
s.authors = ["Mitch VanDuyn"]
|
11
|
+
s.email = ["mitch@catprint.com"]
|
24
12
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
13
|
+
s.summary = "Synchronization of active record models across multiple clients using Pusher, ActionCable, or Polling"
|
14
|
+
s.description = "Work in progress"
|
15
|
+
s.homepage = "https://github.com/reactive-ruby/syncromesh"
|
16
|
+
s.license = "MIT"
|
29
17
|
|
30
|
-
|
31
|
-
|
32
|
-
|
18
|
+
s.files = `git ls-files`.split("\n")
|
19
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) }
|
20
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
21
|
+
s.require_paths = ['lib']
|
33
22
|
end
|
metadata
CHANGED
@@ -1,61 +1,21 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: syncromesh
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Mitch VanDuyn
|
8
8
|
autorequire:
|
9
|
-
bindir:
|
9
|
+
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-05-
|
12
|
-
dependencies:
|
13
|
-
- !ruby/object:Gem::Dependency
|
14
|
-
name: bundler
|
15
|
-
requirement: !ruby/object:Gem::Requirement
|
16
|
-
requirements:
|
17
|
-
- - "~>"
|
18
|
-
- !ruby/object:Gem::Version
|
19
|
-
version: '1.10'
|
20
|
-
type: :development
|
21
|
-
prerelease: false
|
22
|
-
version_requirements: !ruby/object:Gem::Requirement
|
23
|
-
requirements:
|
24
|
-
- - "~>"
|
25
|
-
- !ruby/object:Gem::Version
|
26
|
-
version: '1.10'
|
27
|
-
- !ruby/object:Gem::Dependency
|
28
|
-
name: rake
|
29
|
-
requirement: !ruby/object:Gem::Requirement
|
30
|
-
requirements:
|
31
|
-
- - "~>"
|
32
|
-
- !ruby/object:Gem::Version
|
33
|
-
version: '10.0'
|
34
|
-
type: :development
|
35
|
-
prerelease: false
|
36
|
-
version_requirements: !ruby/object:Gem::Requirement
|
37
|
-
requirements:
|
38
|
-
- - "~>"
|
39
|
-
- !ruby/object:Gem::Version
|
40
|
-
version: '10.0'
|
41
|
-
- !ruby/object:Gem::Dependency
|
42
|
-
name: rspec
|
43
|
-
requirement: !ruby/object:Gem::Requirement
|
44
|
-
requirements:
|
45
|
-
- - ">="
|
46
|
-
- !ruby/object:Gem::Version
|
47
|
-
version: '0'
|
48
|
-
type: :development
|
49
|
-
prerelease: false
|
50
|
-
version_requirements: !ruby/object:Gem::Requirement
|
51
|
-
requirements:
|
52
|
-
- - ">="
|
53
|
-
- !ruby/object:Gem::Version
|
54
|
-
version: '0'
|
11
|
+
date: 2016-05-11 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
55
13
|
description: Work in progress
|
56
14
|
email:
|
57
15
|
- mitch@catprint.com
|
58
|
-
executables:
|
16
|
+
executables:
|
17
|
+
- console
|
18
|
+
- setup
|
59
19
|
extensions: []
|
60
20
|
extra_rdoc_files: []
|
61
21
|
files:
|
@@ -69,8 +29,14 @@ files:
|
|
69
29
|
- Rakefile
|
70
30
|
- bin/console
|
71
31
|
- bin/setup
|
32
|
+
- lib/reactive_record/base.rb
|
33
|
+
- lib/reactive_record/sync_wrapper.rb
|
34
|
+
- lib/reactive_record/syncromesh.rb.erb
|
72
35
|
- lib/syncromesh.rb
|
36
|
+
- lib/syncromesh/configuration.rb
|
73
37
|
- lib/syncromesh/version.rb
|
38
|
+
- spec/spec_helper.rb
|
39
|
+
- spec/syncromesh_spec.rb
|
74
40
|
- syncromesh.gemspec
|
75
41
|
homepage: https://github.com/reactive-ruby/syncromesh
|
76
42
|
licenses:
|
@@ -97,4 +63,6 @@ signing_key:
|
|
97
63
|
specification_version: 4
|
98
64
|
summary: Synchronization of active record models across multiple clients using Pusher,
|
99
65
|
ActionCable, or Polling
|
100
|
-
test_files:
|
66
|
+
test_files:
|
67
|
+
- spec/spec_helper.rb
|
68
|
+
- spec/syncromesh_spec.rb
|