Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

cable_car aka 'Ajax mode' #108

Merged
merged 12 commits into from
Mar 30, 2021
10 changes: 10 additions & 0 deletions lib/cable_ready.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,19 @@
require "cable_ready/version"
require "cable_ready/config"
require "cable_ready/broadcaster"
require "cable_ready/applicable"

module CableReady
class Engine < Rails::Engine
initializer "renderer" do
ActiveSupport.on_load(:action_controller) do
ActionController::Renderers.add :operations do |operations, options|
self.content_type = Mime[:json]
# operations.respond_to?(:ride) ? operations.ride(options) : operations
operations
end
end
end
end

def self.config
Expand Down
24 changes: 24 additions & 0 deletions lib/cable_ready/applicable.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# frozen_string_literal: true

module CableReady
module Applicable
def to_json
enqueued_operations.to_json
end

def apply(operations = "{}")
operations = begin
JSON.parse(operations.is_a?(String) ? operations : operations.to_json)
rescue JSON::ParserError
{}
end
operations.each do |operation_method|
key, operation = operation_method
operation.each do |enqueued_operation|
enqueued_operations[key] << enqueued_operation
end
end
self
end
end
end
5 changes: 5 additions & 0 deletions lib/cable_ready/broadcaster.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# frozen_string_literal: true

require_relative "channels"
require_relative "cable_car"

module CableReady
module Broadcaster
Expand All @@ -10,6 +11,10 @@ def cable_ready
CableReady::Channels.instance
end

def cable_car
CableReady::CableCar.instance
end

def dom_id(record, prefix = nil)
"##{ActionView::RecordIdentifier.dom_id(record, prefix)}"
end
Expand Down
47 changes: 47 additions & 0 deletions lib/cable_ready/cable_car.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# frozen_string_literal: true

require "thread/local"
require_relative "applicable"

module CableReady
class CableCar
extend Thread::Local
include Applicable
attr_reader :enqueued_operations

def initialize
reset
CableReady.config.operation_names.each { |name| add_operation_method name }

config_observer = self
CableReady.config.add_observer config_observer, :add_operation_method
ObjectSpace.define_finalizer self, -> { CableReady.config.delete_observer config_observer }
end

def ride(clear: true)
payload = transmittable_operations
clear ? reset : enqueued_operations.deep_transform_keys! { |key| key.underscore }
payload
end

def add_operation_method(name)
return if respond_to?(name)
singleton_class.public_send :define_method, name, ->(options = {}) {
enqueued_operations[name.to_s] << options.stringify_keys
self # supports operation chaining
}
end

private

def reset
@enqueued_operations = Hash.new { |hash, key| hash[key] = [] }
end

def transmittable_operations
enqueued_operations
.select { |_, list| list.present? }
.deep_transform_keys! { |key| key.to_s.camelize(:lower) }
end
end
end
7 changes: 5 additions & 2 deletions lib/cable_ready/channel.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
# frozen_string_literal: true

require_relative "applicable"

module CableReady
class Channel
include Applicable
attr_reader :identifier, :enqueued_operations

def initialize(identifier)
Expand All @@ -16,12 +19,12 @@ def initialize(identifier)

def broadcast(clear: true)
ActionCable.server.broadcast identifier, {"cableReady" => true, "operations" => broadcastable_operations}
reset if clear
clear ? reset : enqueued_operations.deep_transform_keys! { |key| key.underscore }
end

def broadcast_to(model, clear: true)
identifier.broadcast_to model, {"cableReady" => true, "operations" => broadcastable_operations}
reset if clear
clear ? reset : enqueued_operations.deep_transform_keys! { |key| key.underscore }
end

def add_operation_method(name)
Expand Down
3 changes: 0 additions & 3 deletions lib/cable_ready/channels.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,8 @@ module CableReady
class Channels
extend Thread::Local

attr_accessor :operations

def initialize
@channels = {}
@operations = {}
end

def [](identifier)
Expand Down