collab 0.1.9 → 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
  SHA256:
3
- metadata.gz: 7d18b58e0a43c89ef45deb94871423015a54a755cd565ef86e9025bf5ca50e7a
4
- data.tar.gz: 805a04e74c43e4d2deb0514be61ccce650d2952bac9538f1d1d9721ef9e71678
3
+ metadata.gz: 714939d140c87683e4c7adf35556a020acb7697ce4fb421dadaffb76d2b7c15f
4
+ data.tar.gz: bd24f1cb2b7d137f97f4c66636703f365467be4d915cd1498a1dabc72b958abd
5
5
  SHA512:
6
- metadata.gz: bd446fb6609196d039e53f18387f95da8fbc416a96b2e3e59a18ada3468bcf88957cb1b77b7e49debe0bb4691acc5182a952c9abeceab9e69a07528e8977ba54
7
- data.tar.gz: da896456dce781c41236d76cc914cb21c27e565ba269e0093d8de8d5f05e0a08685af6674a5791753cd94a8499b9274efb8a21bd790b139f4b337975d3fdd475
6
+ metadata.gz: c6f3612a1ad7908d16aa06bccba6c48a87178c6be1aed5824233cdf34a67dc389353b97956167bdce4eb6b4a4e9621910b16e39677963f9dd9a70472d54edc04
7
+ data.tar.gz: '028b45335893ab9d6d59936d9306abe5e5631e635532a2e28e660e4c9b43c674ad2999cc33580e6d997b5ebf88b8ce2e5120cd99d8642a0950b9440a99faa09f'
@@ -0,0 +1,7 @@
1
+ class Collab::CommitJob < ::Collab.config.base_job.constantize
2
+ queue_as :default
3
+
4
+ def perform(document, data)
5
+ document.commits.from_json(data).apply!
6
+ end
7
+ end
@@ -1,19 +1,15 @@
1
- require "collab/railtie"
1
+ require "collab/version"
2
+ require "collab/config"
3
+ require "collab/js"
4
+ require "collab/engine"
2
5
 
3
6
  module Collab
4
- def self.config
5
- @config ||= Collab::Config.new
6
- return @config unless block_given?
7
- yield @config
8
- end
9
-
10
- autoload "Config", "collab/config"
11
- autoload "Bridge", "collab/bridge"
7
+ autoload "Channel", "collab/channel"
12
8
  autoload "HasCollaborativeDocument", "collab/has_collaborative_document"
13
9
 
14
10
  module Models
15
11
  autoload "Base", "collab/models/base"
16
12
  autoload "Document", "collab/models/document"
17
- autoload "DocumentTransaction", "collab/models/document_transaction"
13
+ autoload "Commit", "collab/models/commit"
18
14
  end
19
15
  end
@@ -0,0 +1,33 @@
1
+ module Collab
2
+ module Channel
3
+ def document; @document end
4
+
5
+ def subscribed
6
+ @document = find_document
7
+
8
+ starting_version = params[:startingVersion]&.to_i
9
+ raise "missing startingVersion" if starting_version.nil?
10
+
11
+ stream_for document
12
+
13
+ commits = document.commits
14
+ .where("document_version > ?", starting_version)
15
+ .order(document_version: :asc)
16
+ .load
17
+
18
+ unless commits.empty?
19
+ raise "invalid version" unless commits.first.document_version == (starting_version + 1)
20
+ commits.lazy.map(&:as_json).each(method(:transmit))
21
+ end
22
+ end
23
+
24
+ def commit(data)
25
+ authorize_commit!(data)
26
+ document.commit_later(data)
27
+ end
28
+
29
+ def unsubscribed
30
+ stop_all_streams # this may not be needed
31
+ end
32
+ end
33
+ end
@@ -1,15 +1,34 @@
1
1
  module Collab
2
- class Config
3
- attr_accessor :max_transactions, :application_job, :queue_document_transaction_job_as, :schema_package, :application_record, :document_transaction_model, :document_model
4
-
5
- def find_document_for_subscribe(&block)
6
- @find_document_for_subscribe unless block
7
- @find_document_for_subscribe = block
2
+ @config_mutex = Mutex.new
3
+
4
+ def self.config
5
+ if block_given?
6
+ @config_mutex.synchronize do
7
+ @config ||= ::Collab::Config.new
8
+ raise "[Collab] Tried to configure gem after first use" if @config.frozen?
9
+ yield @config
10
+ end
11
+ else
12
+ raise "[Collab] Missing configuration - Have you run `rails g collab:install` yet?" unless @config
13
+ @config.freeze # really weird stuff could happen if the config changes after first use, so freeze config
8
14
  end
15
+ end
16
+
17
+ class Config
18
+ attr_accessor :base_record,
19
+ :base_job,
20
+ :channel,
21
+ :commit_job,
22
+ :commit_model,
23
+ :document_model,
24
+ :max_commit_history_length,
25
+ :num_js_processes,
26
+ :schema_package
9
27
 
10
- def authorize_update_document(&block)
11
- @authorize_update_document unless block
12
- @authorize_update_document = block
28
+ def initialize
29
+ self.commit_job = "Collab::CommitJob"
30
+ self.document_model = "Collab::Models::Document"
31
+ self.commit_model = "Collab::Models::Commit"
13
32
  end
14
33
  end
15
34
  end
@@ -0,0 +1,6 @@
1
+ if defined?(Rails)
2
+ module Collab
3
+ class Engine < ::Rails::Engine
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,93 @@
1
+ require "json"
2
+
3
+ module Collab
4
+ module JS
5
+ @queue = Queue.new
6
+ @queue_initialized = false
7
+ @queue_initialization_mutex = Mutex.new
8
+
9
+ class <<self
10
+ def queue
11
+ initialize_queue unless @queue_initialized
12
+ @queue
13
+ end
14
+
15
+ # Calls the block given with a JS process acquired from the queue
16
+ # Will block until a JS process is available
17
+ def with_js
18
+ js = queue.pop
19
+ yield js
20
+ ensure
21
+ queue << js
22
+ end
23
+
24
+ def call(name, data = nil, schema_name:)
25
+ with_js { |js| js.call(name, *arguments, &block) }
26
+ end
27
+
28
+ def apply_commit(document, commit, schema_name:)
29
+ call("applyCommit", {doc: document, commit: commit}, schema_name: schema_name)
30
+ end
31
+
32
+ def html_to_document(html, schema_name:)
33
+ call("htmlToDoc", html, schema_name: schema_name)
34
+ end
35
+
36
+ def document_to_html(document, schema_name:)
37
+ call("docToHtml", document, schema_name: schema_name)
38
+ end
39
+
40
+ private
41
+ # Thread-safe initialization of the NodeJS process queue
42
+ def initialize_queue
43
+ @queue_initialization_mutex.synchronize do
44
+ unless @queue_initialized
45
+ ::Collab.config.num_js_processes.times { @queue << ::Collab::JS::JSProcess.new }
46
+ @queue_initialized = true
47
+ end
48
+ end
49
+ end
50
+ end
51
+
52
+ class JSProcess
53
+ def initialize
54
+ @node = if defined?(Rails)
55
+ Dir.chdir(Rails.root) { open_node }
56
+ else
57
+ open_node
58
+ end
59
+ end
60
+
61
+ def call(name, data = nil, schema_name:)
62
+ req = {name: name, data: data, schemaPackage: ::Collab.config.schema_package, schemaName: schema_name}
63
+ @node.puts(JSON.generate(req))
64
+ res = JSON.parse(@node.gets)
65
+ raise ::Collab::JS::JSRuntimeError.new(res["error"]) if res["error"]
66
+ res["result"]
67
+ end
68
+
69
+ private
70
+ def open_node
71
+ IO.popen(["node", "-e", "require('rails-collab-server')"], "r+")
72
+ end
73
+ end
74
+
75
+ class JSRuntimeError < StandardError
76
+ def initialize(data)
77
+ @js_backtrace = data["stack"].split("\n").map{|f| "JavaScript #{f.strip}"} if data["stack"]
78
+
79
+ super(data["name"] + ": " + data["message"])
80
+ end
81
+
82
+ def backtrace
83
+ return unless val = super
84
+
85
+ if @js_backtrace
86
+ @js_backtrace + val
87
+ else
88
+ val
89
+ end
90
+ end
91
+ end
92
+ end
93
+ end
@@ -1,5 +1,5 @@
1
1
  module Collab
2
- class Models::Base < ::Collab.config.application_record.constantize
2
+ class Models::Base < ::Collab.config.base_record.constantize
3
3
  self.abstract_class = true
4
4
  self.table_name_prefix = 'collab_'
5
5
  end
@@ -0,0 +1,53 @@
1
+ module Collab
2
+ class Models::Commit < ::Collab::Models::Base
3
+ belongs_to :document, class_name: ::Collab.config.document_model
4
+
5
+ validates :steps, presence: true
6
+ validates :document_version, presence: true
7
+ validates :ref, length: { maximum: 36 }
8
+
9
+ after_create_commit :broadcast
10
+
11
+ def self.from_json(data)
12
+ new(document_version: data["v"]&.to_i, steps: data["steps"], ref: data["ref"])
13
+ end
14
+
15
+ def as_json
16
+ {
17
+ v: document_version,
18
+ steps: steps,
19
+ ref: ref
20
+ }
21
+ end
22
+
23
+ def apply_later
24
+ raise "cannot apply persisted commit" if self.persisted?
25
+ raise "commit not valid" unless self.valid?
26
+ return false if self.document.document_version != self.document_version
27
+
28
+ ::Collab.config.commit_job.constantize.perform_later(self.document, as_json)
29
+ end
30
+
31
+ def apply!
32
+ raise "cannot apply persisted commit" if self.persisted?
33
+ raise "commit not valid" unless self.valid?
34
+ return false if self.document.document_version != self.document_version # optimization, prevents need to apply lock if outdated
35
+
36
+ self.document.with_lock do
37
+ return false if self.document.document_version != self.document_version
38
+
39
+ return false unless result = ::Collab::JS.apply_commit(self.document, self.to_json, schema_name: self.document.schema_name)
40
+
41
+ self.document.document = result["doc"]
42
+ self.document.document_version = self.document_version
43
+
44
+ self.document.save!
45
+ self.save!
46
+ end
47
+ end
48
+
49
+ def broadcast
50
+ ::Collab.config.channel_name.constantize.broadcast_to(document, as_json)
51
+ end
52
+ end
53
+ end
@@ -1,73 +1,52 @@
1
1
  module Collab
2
2
  class Models::Document < ::Collab::Models::Base
3
3
  belongs_to :attached, polymorphic: true
4
- has_many :transactions, class_name: ::Collab.config.document_transaction_model, foreign_key: :document_id
4
+ has_many :commits, class_name: ::Collab.config.commit_model, foreign_key: :document_id
5
5
 
6
- validates :document, presence: true
6
+ validates :content, presence: true
7
7
  validates :document_version, presence: true, numericality: {only_integer: true, greater_than_or_equal_to: 0}
8
8
  validates :schema_name, presence: true
9
9
 
10
- before_save :nullify_serialized_html, unless: :serialized_html_fresh?
11
- after_save :delete_old_transactions
10
+ after_save :delete_old_commits
12
11
 
13
- # The already-serialized html version of this document
14
- def serialized_html
15
- return super if serialized_html_fresh?
16
- end
17
-
18
- def serialized_html_fresh?
19
- serialized_html_version == document_version
20
- end
21
-
22
- # Serialize the document to html - will be cached if possible (somewhat expensive)
12
+ # Serialize the document to html, uses cached if possible.
13
+ # Note that this may lock the document
23
14
  def to_html
24
15
  return serialized_html if serialized_html
25
16
 
26
- serialized_html = ::Collab::Bridge.current.document_to_html(self.document, schema_name: schema_name)
27
- self.update! serialized_html: serialized_html, serialized_html_version: document_version
28
- serialized_html
17
+ serialized_version = self.document_version
18
+ ::Collab::JS.document_to_html(self.content, schema_name: schema_name).tap do |serialized_html|
19
+ Thread.new do # use a thread to prevent deadlocks and avoid incuring the cost of an inline-write
20
+ self.with_lock do
21
+ self.update_attribute(:serialized_html, serialized_html) if serialized_version == self.version and self.serialized_html.nil?
22
+ end
23
+ end
24
+ end
29
25
  end
30
26
 
31
27
  def from_html(html)
32
- self.document = ::Collab::Bridge.current.html_to_document(html, schema_name: schema_name)
33
- end
34
-
35
- def perform_transaction_later(data)
36
- ::Collab::DocumentTransactionJob.perform_later(@document, data)
28
+ self.content = ::Collab::JS.html_to_document(html, schema_name: schema_name)
37
29
  end
38
30
 
39
- def apply_transaction_now(data)
40
- return unless data["v"].is_a?(Integer)
41
-
42
- return if (data["v"] - 1) != self.document_version # if expired between queue and perform, there is no need to aquire a lock. this is an optimization - not a guarantee
43
- with_lock do
44
- return if (data["v"] - 1) != self.document_version # now that we've aquired a lock to the record, ensure that we're still accessing the correct version
45
-
46
- transaction_result = ::Collab::Bridge.current.apply_transaction(self.document, data, schema_name: self.schema_name)
47
- return unless transaction_result # check to make sure the transaction succeeded
48
-
49
- self.document = transaction_result["doc"]
50
- self.document_version = data["v"]
51
- save!
52
-
53
- transactions.create! document_version: self.document_version, steps: data["steps"], ref: data["ref"]
54
- end
31
+ def commit_later(data)
32
+ commits.from_json(data).apply_later
55
33
  end
56
34
 
57
35
  def as_json
58
- {id: id, document: document, version: self.document_version}
36
+ {id: id, content: content, version: document_version}
59
37
  end
60
38
 
61
- private
62
-
63
- def nullify_serialized_html
39
+ def content_will_change!
40
+ super
64
41
  self.serialized_html = nil
65
42
  end
66
43
 
67
- def delete_old_transactions
68
- cutoff = document_version - ::Collab.config.max_transactions
44
+ private
45
+
46
+ def delete_old_commits
47
+ cutoff = document_version - ::Collab.config.max_commit_history_length
69
48
  return if cutoff <= 0
70
- transactions.where("document_version < ?", cutoff).delete_all
49
+ commits.where("document_version < ?", cutoff).delete_all
71
50
  end
72
51
  end
73
52
  end
@@ -1,3 +1,3 @@
1
1
  module Collab
2
- VERSION = '0.1.9'
2
+ VERSION = '0.3.0'
3
3
  end
@@ -17,11 +17,15 @@ module Collab
17
17
  end
18
18
 
19
19
  def copy_files
20
+ @primary_key_type = Rails.application.config.generators.active_record[:primary_key_type]
21
+
20
22
  migration_template(
21
- "create_collab_tables.rb",
23
+ "create_collab_tables.rb.erb",
22
24
  "db/migrate/create_collab_tables.rb",
23
25
  )
24
26
 
27
+ copy_file "channel.rb", "app/channels/collab_document_channel.rb"
28
+
25
29
  copy_file "initializer.rb", "config/initializers/collab.rb"
26
30
  end
27
31
  end
@@ -0,0 +1,22 @@
1
+ class CollabDocumentChannel < ApplicationCable::Channel
2
+ include Collab::Channel
3
+
4
+ private
5
+
6
+ # Find the document to subscribe to based on the params passed to the channel
7
+ # Authorization may also be performed here (raise an error to prevent subscription)
8
+ def find_document
9
+ Collab::Models::Document.find(params[:document_id]).tap do |document|
10
+ # TODO: Replace with your own authorization logic
11
+ raise "authorization not implemented"
12
+ end
13
+ end
14
+
15
+ # Called a commit is first received for processing
16
+ # Throw an error to prevent the commit from being processed
17
+ # You should consider adding some type of rate-limiting here
18
+ def authorize_commit!(data)
19
+ # TODO: Replace with your own authorization logic
20
+ raise "authorization not implemented"
21
+ end
22
+ end
@@ -0,0 +1,28 @@
1
+ class CreateCollabTables < ActiveRecord::Migration[6.0]
2
+ def change
3
+ create_table :collab_documents<%= ", id: #{@primary_key_type.inspect}" if @primary_key_type %> do |t|
4
+ t.references :attached, null: false, index: false, polymorphic: true<%= ", type: #{@primary_key_type.inspect}" if @primary_key_type %>
5
+ t.string :attached_as, null: false
6
+
7
+ t.index [:attached_type, :attached_id, :attached_as], name: "index_collab_documents_on_attached"
8
+
9
+ t.jsonb :content, null: false
10
+ t.string :schema_name, null: false
11
+ t.integer :document_version, null: false, default: 0
12
+ t.text :serialized_html
13
+
14
+ t.timestamps
15
+ end
16
+
17
+ create_table :collab_commits, id: false do |t|
18
+ t.references :document, null: false, foreign_key: {to_table: :collab_documents}, index: false<%= ", type: #{@primary_key_type.inspect}" if @primary_key_type %>
19
+ t.integer :document_version, null: false
20
+ t.index [:document_id, :document_version], unique: true, order: {document_version: :asc}, name: "index_collab_commits"
21
+
22
+ t.jsonb :steps, array: true, null: false
23
+ t.string :ref
24
+
25
+ t.datetime :created_at, null: false
26
+ end
27
+ end
28
+ end
@@ -4,36 +4,22 @@ Collab.config do |c|
4
4
  # To use a Git repo, see https://docs.npmjs.com/files/package.json#git-urls-as-dependencies
5
5
  c.schema_package = "prosemirror-schema-basic"
6
6
  # How many old transactions to keep per document
7
- c.max_transactions = 250
7
+ c.max_commit_history_length = 250
8
+ # How many NodeJS child processes to run (shared among all threads)
9
+ c.num_js_processes = 3
8
10
 
9
- # Handlers
10
- # ========
11
- # Find a the document to subscribe to based on the params passed to the channel
12
- # Authorization may also be performed here (raise an error)
13
- # The block is executed in the scope of the ActionCable channel within #subscribe
14
- c.find_document_for_subscribe do
15
- Collab::Models::Document.find params[:document_id]
16
- end
17
- # Called when a client submits a transaction in order to update a document
18
- # You should throw an error if unauthorized
19
- # The block is executed in the instance of the channel
20
- c.authorize_update_document do |document, transaction_data|
21
- # raise "authorization failed"
22
- end
11
+ # The document channel to use for collaboration
12
+ # If you change this, you must pass the value as {channel: "[ChannelName]"} in the params from the ActionCable client
13
+ c.channel = "CollabDocumentChannel"
23
14
 
24
- # ActionJob settings
25
- # ==================
26
- # The base job class to use
27
- c.application_job = "::ApplicationJob"
28
- # The job queue to use for DocumentTransaction jobs
29
- c.queue_document_transaction_job_as = :default
15
+ # The class which jobs in the gem should inherit from
16
+ c.base_job = "ApplicationJob"
17
+ # The jobs to use, if you want to implement your own jobs
18
+ # c.commit_job = "..."
30
19
 
31
-
32
- # ActiveRecord settings
33
- # =====================
34
20
  # The class which models in the gem should inherit from
35
- c.application_record = "::ApplicationRecord"
36
- # If you want to use your own document model or document transaction model,
37
- c.document_model = "::Collab::Models::Document"
38
- c.document_transaction_model = "::Collab::Models::DocumentTransaction"
21
+ c.base_record = "ApplicationRecord"
22
+ # The models to use, if you want to implement your own models
23
+ # c.document_model = "..."
24
+ # c.commit_model = "..."
39
25
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: collab
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.9
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ben Aubin
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-07-30 00:00:00.000000000 Z
11
+ date: 2020-08-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -52,19 +52,20 @@ extensions: []
52
52
  extra_rdoc_files: []
53
53
  files:
54
54
  - Rakefile
55
- - app/channels/collab/document_channel.rb
56
- - app/jobs/collab/document_transaction_job.rb
55
+ - app/jobs/collab/commit_job.rb
57
56
  - lib/collab.rb
58
- - lib/collab/bridge.rb
57
+ - lib/collab/channel.rb
59
58
  - lib/collab/config.rb
59
+ - lib/collab/engine.rb
60
60
  - lib/collab/has_collaborative_document.rb
61
+ - lib/collab/js.rb
61
62
  - lib/collab/models/base.rb
63
+ - lib/collab/models/commit.rb
62
64
  - lib/collab/models/document.rb
63
- - lib/collab/models/document_transaction.rb
64
- - lib/collab/railtie.rb
65
65
  - lib/collab/version.rb
66
66
  - lib/generators/collab/install/install_generator.rb
67
- - lib/generators/collab/install/templates/create_collab_tables.rb
67
+ - lib/generators/collab/install/templates/channel.rb
68
+ - lib/generators/collab/install/templates/create_collab_tables.rb.erb
68
69
  - lib/generators/collab/install/templates/initializer.rb
69
70
  - lib/tasks/collab_tasks.rake
70
71
  homepage: https://github.com/benaubin/rails-collab
@@ -1,28 +0,0 @@
1
- class Collab::DocumentChannel < ApplicationCable::Channel
2
- def subscribed
3
- @document = instance_exec(::Collab.config.find_document_for_subscribe)
4
-
5
- starting_version = params[:startingVersion]&.to_i
6
- raise "missing startingVersion" if starting_version.nil?
7
-
8
- stream_for @document
9
-
10
- transactions = @document.transactions
11
- .where("document_version > ?", starting_version)
12
- .order(document_version: :asc)
13
- .load
14
-
15
- raise "invalid version" unless transactions.first.document_version == (starting_version + 1) unless transactions.empty?
16
-
17
- transactions.lazy.map(&:as_json).each(method(:transmit))
18
- end
19
-
20
- def submit(data)
21
- instance_exec(@document, data, &::Collab.config.authorize_update_document)
22
- @document.perform_transaction_later(data)
23
- end
24
-
25
- def unsubscribed
26
- stop_all_streams # this may not be needed
27
- end
28
- end
@@ -1,7 +0,0 @@
1
- class Collab::DocumentTransactionJob < ::Collab.config.application_job.constantize
2
- queue_as ::Collab.config.queue_document_transaction_job_as
3
-
4
- def perform(document, data)
5
- document.apply_transaction_now(data)
6
- end
7
- end
@@ -1,54 +0,0 @@
1
- require "collab/config"
2
- require "json"
3
-
4
- module Collab
5
- class Bridge
6
- class JSRuntimeError < StandardError
7
- def initialize(data)
8
- @js_backtrace = data["stack"].split("\n").map{|f| "JavaScript #{f.strip}"} if data["stack"]
9
-
10
- super(data["name"] + ": " + data["message"])
11
- end
12
-
13
- def backtrace
14
- return unless val = super
15
-
16
- if @js_backtrace
17
- @js_backtrace + val
18
- else
19
- val
20
- end
21
- end
22
- end
23
-
24
- def initialize
25
- @node = Dir.chdir(Rails.root) do
26
- IO.popen(["node", "-e", "require('rails-collab-server')"], "r+")
27
- end
28
- end
29
-
30
- def self.current
31
- @current ||= new
32
- end
33
-
34
- def call(name, data = nil, schema_name:)
35
- req = {name: name, data: data, schemaPackage: ::Collab.config.schema_package, schemaName: schema_name}
36
- @node.puts(JSON.generate(req))
37
- res = JSON.parse(@node.gets)
38
- raise ::Collab::Bridge::JSRuntimeError.new(res["error"]) if res["error"]
39
- res["result"]
40
- end
41
-
42
- def apply_transaction(document, transaction, schema_name:)
43
- call("applyTransaction", {doc: document, data: transaction}, schema_name: schema_name)
44
- end
45
-
46
- def html_to_document(html, schema_name:)
47
- call("htmlToDoc", html, schema_name: schema_name)
48
- end
49
-
50
- def document_to_html(document, schema_name:)
51
- call("docToHtml", document, schema_name: schema_name)
52
- end
53
- end
54
- end
@@ -1,22 +0,0 @@
1
- module Collab
2
- class Models::DocumentTransaction < ::Collab::Models::Base
3
- belongs_to :document, class_name: ::Collab.config.document_model
4
-
5
- validates :steps, presence: true
6
- validates :document_version, presence: true
7
-
8
- after_create_commit :broadcast
9
-
10
- def as_json
11
- {
12
- v: document_version,
13
- steps: steps,
14
- ref: ref
15
- }
16
- end
17
-
18
- def broadcast
19
- ::Collab::DocumentChannel.broadcast_to(document, as_json)
20
- end
21
- end
22
- end
@@ -1,4 +0,0 @@
1
- module Collab
2
- class Railtie < ::Rails::Railtie
3
- end
4
- end
@@ -1,32 +0,0 @@
1
- class CreateCollabTables < ActiveRecord::Migration[6.0]
2
- def change
3
- create_table :collab_documents, id: :uuid do |t|
4
- t.references :attached, null: false, index: false, type: :uuid, polymorphic: true
5
- t.string :attached_as
6
- t.index [:attached_type, :attached_id, :attached_as], name: "index_collaborative_documents_on_attached"
7
-
8
- t.string :schema_name, null: false
9
-
10
- t.jsonb :document, null: false
11
- t.integer :document_version, null: false, default: 0
12
-
13
- t.text :serialized_html
14
- t.integer :serialized_html_version
15
-
16
- t.timestamps
17
- end
18
-
19
- create_table :collab_document_transactions, id: false do |t|
20
- t.references :document, null: false, foreign_key: {to_table: :collab_documents}, type: :uuid, index: false
21
-
22
- t.jsonb :steps, array: true, null: false
23
- t.integer :document_version, null: false
24
-
25
- t.string :ref
26
-
27
- t.index [:document_id, :document_version], unique: true, order: {document_version: :asc}, name: "index_collaborative_document_transactions"
28
-
29
- t.timestamps
30
- end
31
- end
32
- end