anycable 0.0.1 → 0.1.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: 281158d65c6c109cf2b79f1bc2094e97ff2a35a3
4
- data.tar.gz: b66137f82d301f38d0ebecfce186175848748a26
3
+ metadata.gz: 1728535555e8fb2b4117c3acc46a37c99f854b20
4
+ data.tar.gz: 50344774fb497a690c610a6e4d2758ab8237aa89
5
5
  SHA512:
6
- metadata.gz: 24a693ba7d9bf4378d7b990d520289721565cec10f324849c4f2c74cbb0f09204a5eb5fd65babea3e2752e9d91696d58b1a875240c2aa00dd5c846483addf02f
7
- data.tar.gz: 06fd5a2620adaada64272eaca7390a73d9e47a1b7f9df1f2192b2a0c916e2080c91a1ef5746dbcc269bdb82bbba33bdd288707d2ff39b80076110ac2eabd6fc6
6
+ metadata.gz: d1679838e7116f199f29e1ef51d156422da51daa52c6f6b61e42adf0d1d17e36c47f45614fa05f400757a3811d80e03ca26196cca1055c314a3f7b97b9dec523
7
+ data.tar.gz: 3cf4eb06da615053c914b86da466d77f3f4f9bc84265d3fcb4079ebd8e30ce451d9bbd2cf5fa62637f543bfc3fbe0f5ba21594e0870bef9e6c1fe6b53ad70c66
data/.rubocop.yml CHANGED
@@ -9,6 +9,7 @@ AllCops:
9
9
  - 'spec/dummy/**/*'
10
10
  - 'tmp/**/*'
11
11
  - 'bench/**/*'
12
+ - 'lib/anycable/rpc/**/*'
12
13
  DisplayCopNames: true
13
14
  StyleGuideCopsOnly: false
14
15
  TargetRubyVersion: 2.3
data/Makefile ADDED
@@ -0,0 +1,4 @@
1
+ all: build
2
+
3
+ build:
4
+ protoc --ruby_out=./lib/anycable/rpc --grpc_out=./lib/anycable/rpc --proto_path=./protos --plugin=protoc-gen-grpc=`which grpc_tools_ruby_protoc_plugin.rb` ./protos/rpc.proto
data/README.md CHANGED
@@ -2,15 +2,87 @@
2
2
 
3
3
  # Anycable
4
4
 
5
+ AnyCable allows you to use any WebSocket server (written in any language) as a replacement for built-in Ruby ActionCable server.
6
+
7
+ With AnyCable you can use channels, client-side JS, broadcasting - (almost) all that you can do with ActionCable.
8
+
9
+ You can even use ActionCable in development and not be afraid of compatibility issues.
10
+
11
+ ## Requirements
12
+
13
+ - Ruby ~> 2.3;
14
+ - Rails ~> 5.0;
15
+ - Redis
16
+
17
+ ## How It Works?
18
+
5
19
  TBD
6
20
 
7
- ## Usage
21
+ ## Compatible WebSocket servers
8
22
 
9
23
  TBD
10
24
 
25
+
11
26
  ## Installation
12
27
 
13
- TBD
28
+ Add Anycable to your application's Gemfile:
29
+
30
+ ```ruby
31
+ gem 'anycable', group: :production
32
+ ```
33
+
34
+ And then run:
35
+
36
+ ```shell
37
+ rails generate anycable
38
+ ```
39
+
40
+ to create executable.
41
+
42
+ You can use _built-in_ ActionCable for test and development.
43
+
44
+ ## Configuration
45
+
46
+ Add `config/anycable.yml`if you want to override defaults (see below):
47
+
48
+ ```yml
49
+ production:
50
+ # gRPC server host and port
51
+ rpc_host: "localhost:50051"
52
+ # Redis URL (for broadcasting)
53
+ redis_url: "redis://localhost:6379/2"
54
+ # Redis channel name
55
+ redis_channel: "anycable"
56
+
57
+ ```
58
+
59
+ Anycable uses [anyway_config](https://github.com/palkan/anyway_config), thus it is also possible to set configuration variables through `secrets.yml` or environment vars.
60
+
61
+ ## Usage
62
+
63
+ Run Anycable server:
64
+
65
+ ```ruby
66
+ bundle exec anycable
67
+ ```
68
+
69
+ ## ActionCable Compatibility
70
+
71
+
72
+ Feature | Status
73
+ -------------------------|--------
74
+ Connection Identifiers | +
75
+ Connection Request (cookies, params) | +
76
+ Disconnect Handling | coming soon
77
+ Subscribe to channels | +
78
+ Parameterized subscriptions | coming soon
79
+ Unsubscribe from channels | +
80
+ [Subscription Instance Variables](http://edgeapi.rubyonrails.org/classes/ActionCable/Channel/Streams.html) | -
81
+ Performing Channel Actions | +
82
+ Streaming | +
83
+ [Custom stream callbacks](http://edgeapi.rubyonrails.org/classes/ActionCable/Channel/Streams.html) | -
84
+ Broadcasting | +
85
+
14
86
 
15
87
  ## Contributing
16
88
 
data/Rakefile CHANGED
@@ -2,3 +2,5 @@ require "bundler/gem_tasks"
2
2
  require "rspec/core/rake_task"
3
3
 
4
4
  RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task default: :spec
data/anycable.gemspec CHANGED
@@ -18,6 +18,9 @@ Gem::Specification.new do |spec|
18
18
  spec.require_paths = ["lib"]
19
19
 
20
20
  spec.add_dependency "rails", "~> 5"
21
+ spec.add_dependency "anyway_config", "~>0.4.0"
22
+ spec.add_dependency "grpc", "~> 1.0"
23
+ spec.add_dependency "redis", "~> 3.0"
21
24
 
22
25
  spec.add_development_dependency "bundler", "~> 1"
23
26
  spec.add_development_dependency "rake", "~> 10.0"
data/bin/console ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "rails"
5
+ require "anycable/server"
6
+
7
+ require "pry"
8
+ Pry.start
data/lib/anycable.rb CHANGED
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
  require "anycable/version"
3
+ require "anycable/config"
4
+ require "anycable/actioncable/server"
3
5
 
4
6
  # Anycable allows to use any websocket service (written in any language) as a replacement
5
7
  # for ActionCable server.
@@ -9,4 +11,19 @@ require "anycable/version"
9
11
  #
10
12
  # Broadcasting messages to WS is done through Redis Pub/Sub.
11
13
  module Anycable
14
+ def self.logger=(logger)
15
+ @logger = logger
16
+ end
17
+
18
+ def self.logger
19
+ @logger ||= Anycable.config.debug ? Logger.new(STDOUT) : Logger.new('/dev/null')
20
+ end
21
+
22
+ def self.config
23
+ @config ||= Config.new
24
+ end
25
+
26
+ def self.configure
27
+ yield(config) if block_given?
28
+ end
12
29
  end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+ require "action_cable"
3
+
4
+ module ActionCable
5
+ module Channel
6
+ class Base # :nodoc:
7
+ alias do_subscribe subscribe_to_channel
8
+
9
+ public :do_subscribe, :subscription_rejected?
10
+
11
+ def subscribe_to_channel
12
+ # noop
13
+ end
14
+
15
+ attr_reader :stop_streams
16
+
17
+ def stream_from(broadcasting, callback = nil, coder: nil)
18
+ raise ArgumentError('Unsupported') if callback.present? || coder.present? || block_given?
19
+ streams << broadcasting
20
+ end
21
+
22
+ def stop_all_streams
23
+ @stop_streams = true
24
+ end
25
+
26
+ def streams
27
+ @streams ||= []
28
+ end
29
+
30
+ def stop_streams?
31
+ stop_streams == true
32
+ end
33
+
34
+ def delegate_connection_identifiers
35
+ connection.identifiers.each do |identifier|
36
+ define_singleton_method(identifier) do
37
+ connection.fetch_identifier(identifier)
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,72 @@
1
+ # frozen_string_literal: true
2
+ require "action_cable"
3
+
4
+ module ActionCable
5
+ module Connection
6
+ class Base # :nodoc:
7
+ attr_reader :transmissions
8
+
9
+ def initialize(env: {}, identifiers_json: '{}')
10
+ @ids = ActiveSupport::JSON.decode(identifiers_json)
11
+ @cached_ids = {}
12
+ @env = env
13
+ @coder = ActiveSupport::JSON
14
+ @closed = false
15
+ @transmissions = []
16
+ @subscriptions = ActionCable::Connection::Subscriptions.new(self)
17
+ end
18
+
19
+ def handle_open
20
+ connect if respond_to?(:connect)
21
+ send_welcome_message
22
+ rescue ActionCable::Connection::Authorization::UnauthorizedError
23
+ close
24
+ end
25
+
26
+ def handle_close
27
+ # subscriptions.unsubscribe_from_all
28
+ disconnect if respond_to?(:disconnect)
29
+ end
30
+
31
+ def close
32
+ @closed = true
33
+ end
34
+
35
+ def closed?
36
+ @closed
37
+ end
38
+
39
+ def transmit(cable_message)
40
+ transmissions << encode(cable_message)
41
+ end
42
+
43
+ def dispose
44
+ @closed = false
45
+ transmissions.clear
46
+ end
47
+
48
+ # Generate identifiers info.
49
+ # Converts GlobalID compatible vars to corresponding global IDs params.
50
+ def identifiers_hash
51
+ identifiers.each_with_object({}) do |id, acc|
52
+ obj = instance_variable_get("@#{id}")
53
+ next unless obj
54
+ acc[id] = obj.try(:to_gid_param) || obj
55
+ end
56
+ end
57
+
58
+ # Fetch identifier and deserialize if neccessary
59
+ def fetch_identifier(name)
60
+ @cached_ids[name] ||= @cached_ids.fetch(name) do
61
+ val = @ids[name.to_s]
62
+ next val unless val.is_a?(String)
63
+ GlobalID::Locator.locate(val) || val
64
+ end
65
+ end
66
+
67
+ def logger
68
+ ::Rails.logger
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+ require "action_cable"
3
+ require "anycable/pubsub"
4
+
5
+ module ActionCable
6
+ module Server
7
+ # Override pubsub for ActionCable
8
+ class Base
9
+ def pubsub
10
+ @any_pubsub ||= Anycable::PubSub.new
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+ require "anyway"
3
+
4
+ module Anycable
5
+ # Anycable configuration
6
+ class Config < Anyway::Config
7
+ config_name :anycable
8
+
9
+ attr_config rpc_host: "localhost:50051",
10
+ redis_url: "redis://localhost:6379/5",
11
+ redis_channel: "anycable",
12
+ debug: false
13
+ end
14
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+ require "redis"
3
+
4
+ module Anycable
5
+ # PubSub for broadcasting
6
+ class PubSub
7
+ attr_reader :redis_conn
8
+
9
+ def initialize
10
+ @redis_conn = Redis.new(url: Anycable.config.redis_url)
11
+ end
12
+
13
+ def broadcast(channel, payload)
14
+ redis_conn.publish(
15
+ Anycable.config.redis_channel,
16
+ { stream: channel, data: payload }.to_json
17
+ )
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,51 @@
1
+ # Generated by the protocol buffer compiler. DO NOT EDIT!
2
+ # source: rpc.proto
3
+
4
+ require 'google/protobuf'
5
+
6
+ Google::Protobuf::DescriptorPool.generated_pool.build do
7
+ add_message "anycable.ConnectionRequest" do
8
+ optional :path, :string, 1
9
+ map :headers, :string, :string, 2
10
+ end
11
+ add_message "anycable.ConnectionResponse" do
12
+ optional :status, :enum, 1, "anycable.Status"
13
+ optional :identifiers, :string, 2
14
+ repeated :transmissions, :string, 3
15
+ end
16
+ add_message "anycable.CommandMessage" do
17
+ optional :command, :string, 1
18
+ optional :identifier, :string, 2
19
+ optional :connection_identifiers, :string, 3
20
+ optional :data, :string, 4
21
+ end
22
+ add_message "anycable.CommandResponse" do
23
+ optional :status, :enum, 1, "anycable.Status"
24
+ optional :disconnect, :bool, 2
25
+ optional :stop_streams, :bool, 3
26
+ optional :stream_from, :bool, 4
27
+ optional :stream_id, :string, 5
28
+ repeated :transmissions, :string, 6
29
+ end
30
+ add_message "anycable.DisconnectRequest" do
31
+ optional :identifiers, :string, 1
32
+ repeated :subscriptions, :string, 2
33
+ end
34
+ add_message "anycable.DisconnectResponse" do
35
+ optional :status, :enum, 1, "anycable.Status"
36
+ end
37
+ add_enum "anycable.Status" do
38
+ value :ERROR, 0
39
+ value :SUCCESS, 1
40
+ end
41
+ end
42
+
43
+ module Anycable
44
+ ConnectionRequest = Google::Protobuf::DescriptorPool.generated_pool.lookup("anycable.ConnectionRequest").msgclass
45
+ ConnectionResponse = Google::Protobuf::DescriptorPool.generated_pool.lookup("anycable.ConnectionResponse").msgclass
46
+ CommandMessage = Google::Protobuf::DescriptorPool.generated_pool.lookup("anycable.CommandMessage").msgclass
47
+ CommandResponse = Google::Protobuf::DescriptorPool.generated_pool.lookup("anycable.CommandResponse").msgclass
48
+ DisconnectRequest = Google::Protobuf::DescriptorPool.generated_pool.lookup("anycable.DisconnectRequest").msgclass
49
+ DisconnectResponse = Google::Protobuf::DescriptorPool.generated_pool.lookup("anycable.DisconnectResponse").msgclass
50
+ Status = Google::Protobuf::DescriptorPool.generated_pool.lookup("anycable.Status").enummodule
51
+ end
@@ -0,0 +1,25 @@
1
+ # Generated by the protocol buffer compiler. DO NOT EDIT!
2
+ # Source: rpc.proto for package 'anycable'
3
+
4
+ require 'grpc'
5
+ require_relative 'rpc'
6
+
7
+ module Anycable
8
+ module RPC
9
+ class Service
10
+ include GRPC::GenericService
11
+
12
+ self.marshal_class_method = :encode
13
+ self.unmarshal_class_method = :decode
14
+ self.service_name = 'anycable.RPC'
15
+
16
+ rpc :Connect, ConnectionRequest, ConnectionResponse
17
+ rpc :Subscribe, CommandMessage, CommandResponse
18
+ rpc :Unsubscribe, CommandMessage, CommandResponse
19
+ rpc :Perform, CommandMessage, CommandResponse
20
+ rpc :Disconnect, DisconnectRequest, DisconnectResponse
21
+ end
22
+
23
+ Stub = Service.rpc_stub_class
24
+ end
25
+ end
@@ -0,0 +1,147 @@
1
+ # frozen_string_literal: true
2
+ require 'anycable/actioncable/connection'
3
+ require 'anycable/actioncable/channel'
4
+ require 'anycable/rpc/rpc'
5
+ require 'anycable/rpc/rpc_services'
6
+
7
+ # rubocop:disable Metrics/ClassLength
8
+ # rubocop:disable Metrics/AbcSize
9
+ # rubocop:disable Metrics/MethodLength
10
+ module Anycable
11
+ # RPC service handler
12
+ class RPCHandler < Anycable::RPC::Service
13
+ # Handle connection request from WebSocket server
14
+ def connect(request, _unused_call)
15
+ logger.debug("RPC Connect: #{request}")
16
+
17
+ connection = ApplicationCable::Connection.new(
18
+ env:
19
+ path_env(request.path).merge(
20
+ 'HTTP_COOKIE' => request.headers['Cookie']
21
+ )
22
+ )
23
+
24
+ connection.handle_open
25
+
26
+ if connection.closed?
27
+ Anycable::ConnectionResponse.new(status: Anycable::Status::ERROR)
28
+ else
29
+ Anycable::ConnectionResponse.new(
30
+ status: Anycable::Status::SUCCESS,
31
+ identifiers: connection.identifiers_hash.to_json,
32
+ transmissions: connection.transmissions
33
+ )
34
+ end
35
+ end
36
+
37
+ def disconnect(request, _unused_call)
38
+ logger.debug("RPC Disonnect: #{request}")
39
+ # TODO: implement disconnect logic
40
+ Anycable::DisconnectResponse.new(status: Anycable::Status::SUCCESS)
41
+ end
42
+
43
+ def subscribe(message, _unused_call)
44
+ logger.debug("RPC Subscribe: #{message}")
45
+ connection = ApplicationCable::Connection.new(
46
+ identifiers_json: message.connection_identifiers
47
+ )
48
+
49
+ channel = channel_for(connection, message)
50
+
51
+ if channel.present?
52
+ channel.do_subscribe
53
+ if channel.subscription_rejected?
54
+ Anycable::CommandResponse.new(
55
+ status: Anycable::Status::ERROR,
56
+ disconnect: connection.closed?,
57
+ transmissions: connection.transmissions
58
+ )
59
+ else
60
+ Anycable::CommandResponse.new(
61
+ status: Anycable::Status::SUCCESS,
62
+ disconnect: connection.closed?,
63
+ stop_streams: channel.stop_streams?,
64
+ stream_from: channel.streams.present?,
65
+ stream_id: channel.streams.first || '',
66
+ transmissions: connection.transmissions
67
+ )
68
+ end
69
+ else
70
+ Anycable::CommandResponse.new(
71
+ status: Anycable::Status::ERROR
72
+ )
73
+ end
74
+ end
75
+
76
+ def unsubscribe(message, _unused_call)
77
+ logger.debug("RPC Unsubscribe: #{message}")
78
+ Anycable::CommandResponse.new(
79
+ status: Anycable::Status::SUCCESS,
80
+ disconnect: false,
81
+ stop_streams: true,
82
+ stream_from: false
83
+ )
84
+ end
85
+
86
+ def perform(message, _unused_call)
87
+ logger.debug("RPC Perform: #{message}")
88
+ connection = ApplicationCable::Connection.new(
89
+ identifiers_json: message.connection_identifiers
90
+ )
91
+
92
+ channel = channel_for(connection, message)
93
+
94
+ if channel.present?
95
+ channel.perform_action(ActiveSupport::JSON.decode(message.data))
96
+ Anycable::CommandResponse.new(
97
+ status: Anycable::Status::SUCCESS,
98
+ disconnect: connection.closed?,
99
+ stop_streams: channel.stop_streams?,
100
+ stream_from: channel.streams.present?,
101
+ stream_id: channel.streams.first || '',
102
+ transmissions: connection.transmissions
103
+ )
104
+ else
105
+ Anycable::CommandResponse.new(
106
+ status: Anycable::Status::ERROR
107
+ )
108
+ end
109
+ end
110
+
111
+ private
112
+
113
+ # Build env from path
114
+ def path_env(path)
115
+ uri = URI.parse(path)
116
+ {
117
+ 'QUERY_STRING' => uri.query,
118
+ 'SCRIPT_NAME' => '',
119
+ 'PATH_INFO' => uri.path,
120
+ 'SERVER_PORT' => uri.port.to_s,
121
+ 'HTTP_HOST' => uri.host,
122
+ # Hack to avoid Missing rack.input error
123
+ 'rack.request.form_input' => '',
124
+ 'rack.input' => '',
125
+ 'rack.request.form_hash' => {}
126
+ }
127
+ end
128
+
129
+ def channel_for(connection, message)
130
+ id_key = message.identifier
131
+ id_options = ActiveSupport::JSON.decode(id_key).with_indifferent_access
132
+
133
+ subscription_klass = id_options[:channel].safe_constantize
134
+
135
+ if subscription_klass
136
+ subscription_klass.new(connection, id_key, id_options)
137
+ else
138
+ logger.error "Subscription class not found (#{message.inspect})"
139
+ nil
140
+ end
141
+ end
142
+
143
+ def logger
144
+ Anycable.logger
145
+ end
146
+ end
147
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+ require 'grpc'
3
+ require 'anycable'
4
+ require 'anycable/rpc_handler'
5
+
6
+ # Set GRPC logger
7
+ module GRPC
8
+ def self.logger
9
+ Anycable.logger
10
+ end
11
+ end
12
+
13
+ module Anycable
14
+ # Wrapper over GRPC server
15
+ module Server
16
+ class << self
17
+ attr_accessor :grpc_server
18
+
19
+ def start
20
+ @grpc_server = GRPC::RpcServer.new
21
+ grpc_server.add_http2_port(Anycable.config.rpc_host, :this_port_is_insecure)
22
+ grpc_server.handle(Anycable::RPCHandler)
23
+ Anycable.logger.info "RPC server is listening on #{Anycable.config.rpc_host}"
24
+ grpc_server.run_till_terminated
25
+ end
26
+ end
27
+ end
28
+ end
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  module Anycable
3
- VERSION = "0.0.1"
3
+ VERSION = "0.1.0"
4
4
  end
@@ -0,0 +1,7 @@
1
+ Description:
2
+ Generates Anycable executable
3
+
4
+ Examples:
5
+ rails generate anycable
6
+
7
+ This will generate Anycable executable
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+ require "rails/generators/base"
3
+
4
+ class AnycableGenerator < Rails::Generators::Base # :nodoc:
5
+ source_root File.expand_path('../templates', __FILE__)
6
+
7
+ def create_executable_file
8
+ template "script", "bin/anycable"
9
+ chmod "bin/anycable", 0o755
10
+ end
11
+ end
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+ require ::File.expand_path('../../config/environment', __FILE__)
4
+ Rails.application.eager_load!
5
+
6
+ require 'anycable/server'
7
+
8
+ Anycable::Server.start
@@ -1,5 +1,17 @@
1
1
  # frozen_string_literal: true
2
- # desc "Explaining what the task does"
3
- # task :anycable do
4
- # # Task goes here
5
- # end
2
+ namespace :anycable do
3
+ desc "Make test gRPC call"
4
+ task check: :environment do
5
+ require 'grpc'
6
+ require 'anycable/rpc/rpc_services'
7
+
8
+ Anycable.logger = Logger.new(STDOUT)
9
+ stub = Anycable::RPC::Stub.new(Anycable.config.rpc_host, :this_channel_is_insecure)
10
+ stub.connect(
11
+ Anycable::ConnectionRequest.new(
12
+ path: 'http://example.com',
13
+ headers: { 'Cookie' => 'test=1;' }
14
+ )
15
+ )
16
+ end
17
+ end
data/protos/rpc.proto ADDED
@@ -0,0 +1,52 @@
1
+ syntax = "proto3";
2
+
3
+ package anycable;
4
+
5
+ service RPC {
6
+ rpc Connect (ConnectionRequest) returns (ConnectionResponse) {}
7
+ rpc Subscribe (CommandMessage) returns (CommandResponse) {}
8
+ rpc Unsubscribe (CommandMessage) returns (CommandResponse) {}
9
+ rpc Perform (CommandMessage) returns (CommandResponse) {}
10
+ rpc Disconnect (DisconnectRequest) returns (DisconnectResponse) {}
11
+ }
12
+
13
+ enum Status {
14
+ ERROR = 0;
15
+ SUCCESS = 1;
16
+ }
17
+
18
+ message ConnectionRequest {
19
+ string path = 1;
20
+ map<string,string> headers = 2;
21
+ }
22
+
23
+ message ConnectionResponse {
24
+ Status status = 1;
25
+ string identifiers = 2;
26
+ repeated string transmissions = 3;
27
+ }
28
+
29
+ message CommandMessage {
30
+ string command = 1;
31
+ string identifier = 2;
32
+ string connection_identifiers = 3;
33
+ string data = 4;
34
+ }
35
+
36
+ message CommandResponse {
37
+ Status status = 1;
38
+ bool disconnect = 2;
39
+ bool stop_streams = 3;
40
+ bool stream_from = 4;
41
+ string stream_id = 5;
42
+ repeated string transmissions = 6;
43
+ }
44
+
45
+ message DisconnectRequest {
46
+ string identifiers = 1;
47
+ repeated string subscriptions = 2;
48
+ }
49
+
50
+ message DisconnectResponse {
51
+ Status status = 1;
52
+ }
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: anycable
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - palkan
@@ -24,6 +24,48 @@ dependencies:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
26
  version: '5'
27
+ - !ruby/object:Gem::Dependency
28
+ name: anyway_config
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 0.4.0
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: 0.4.0
41
+ - !ruby/object:Gem::Dependency
42
+ name: grpc
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1.0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: redis
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '3.0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '3.0'
27
69
  - !ruby/object:Gem::Dependency
28
70
  name: bundler
29
71
  requirement: !ruby/object:Gem::Requirement
@@ -108,14 +150,29 @@ files:
108
150
  - CHANGELOG.md
109
151
  - Gemfile
110
152
  - MIT-LICENSE
153
+ - Makefile
111
154
  - README.md
112
155
  - Rakefile
113
156
  - anycable.gemspec
157
+ - bin/console
114
158
  - bin/setup
115
159
  - circle.yml
116
160
  - lib/anycable.rb
161
+ - lib/anycable/actioncable/channel.rb
162
+ - lib/anycable/actioncable/connection.rb
163
+ - lib/anycable/actioncable/server.rb
164
+ - lib/anycable/config.rb
165
+ - lib/anycable/pubsub.rb
166
+ - lib/anycable/rpc/rpc.rb
167
+ - lib/anycable/rpc/rpc_services.rb
168
+ - lib/anycable/rpc_handler.rb
169
+ - lib/anycable/server.rb
117
170
  - lib/anycable/version.rb
171
+ - lib/generators/anycable/USAGE
172
+ - lib/generators/anycable/anycable_generator.rb
173
+ - lib/generators/anycable/templates/script
118
174
  - lib/tasks/anycable_tasks.rake
175
+ - protos/rpc.proto
119
176
  homepage: http://github.com/anycable/anycable
120
177
  licenses:
121
178
  - MIT