anycable 0.0.1 → 0.1.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: 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