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 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