syncromesh 0.1.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: aa5bbeb8bc188c3196e791fddac1bcee5846cf23
4
- data.tar.gz: e6c341a3dddee5d485b4d93f1e756f75cca698c8
3
+ metadata.gz: 0d803e6d6129ea7733ea11227a18b3e589d80fc9
4
+ data.tar.gz: 2ce570a9ff38672c527436b13cbf6b52d5ddaec3
5
5
  SHA512:
6
- metadata.gz: 1c4c32b48279504496129f163e5762895160d9b111d5cc7ae436c3a42f9bdb03e0083b797d3dcc533e8c0e4882d3f25330599d73c042fc5a480d6401ca681dd8
7
- data.tar.gz: 9fc1bae19495895bcdbf46ce7628416c91df13cfca715f81d17eab4ff56e4b21eeadf0ec2d42858ae3437c51fdcdc97dd1f5c360c6ad74284a0f1717275aae4a
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,18 @@
1
+ module ReactiveRecord
2
+
3
+ class SyncWrapper
4
+
5
+ def initialize(model)
6
+ @model = model
7
+ end
8
+
9
+ def where(opts = {})
10
+ opts.each do |attr, value|
11
+ return false unless @model.send(attr) == value
12
+ end
13
+ true
14
+ end
15
+
16
+ end
17
+
18
+ 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
@@ -1,5 +1,176 @@
1
- require "syncromesh/version"
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
@@ -1,3 +1,3 @@
1
1
  module Syncromesh
2
- VERSION = "0.1.0"
2
+ VERSION = "0.3.0"
3
3
  end
@@ -0,0 +1,2 @@
1
+ $LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
2
+ require 'syncromesh'
@@ -0,0 +1,11 @@
1
+ require 'spec_helper'
2
+
3
+ describe Syncromesh do
4
+ it 'has a version number' do
5
+ expect(Syncromesh::VERSION).not_to be nil
6
+ end
7
+
8
+ it 'does something useful' do
9
+ expect(false).to eq(true)
10
+ end
11
+ end
@@ -1,33 +1,22 @@
1
- # coding: utf-8
2
- lib = File.expand_path('../lib', __FILE__)
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
- Gem::Specification.new do |spec|
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
- spec.summary = "Synchronization of active record models across multiple clients using Pusher, ActionCable, or Polling"
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
- # Prevent pushing this gem to RubyGems.org by setting 'allowed_push_host', or
18
- # delete this section to allow pushing this gem to any host.
19
- # if spec.respond_to?(:metadata)
20
- # spec.metadata['allowed_push_host'] = "TODO: Set to 'http://mygemserver.com'"
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
- spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
26
- spec.bindir = "exe"
27
- spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
28
- spec.require_paths = ["lib"]
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
- spec.add_development_dependency "bundler", "~> 1.10"
31
- spec.add_development_dependency "rake", "~> 10.0"
32
- spec.add_development_dependency "rspec"
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.1.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mitch VanDuyn
8
8
  autorequire:
9
- bindir: exe
9
+ bindir: bin
10
10
  cert_chain: []
11
- date: 2016-05-09 00:00:00.000000000 Z
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