anycable / anycable-rails

AnyCable for Ruby on Rails applications
https://anycable.io
MIT License
499 stars 35 forks source link

Channel state in 1.0 #134

Closed alecdotninja closed 4 years ago

alecdotninja commented 4 years ago

Tell us about your environment

Ruby version: ruby 2.6.6p146 (2020-03-31 revision 67876) [x86_64-linux]

Rails version: 6.0.3.2

anycable gem version: 1.0.0

anycable-rails gem version: 1.0.0

grpc gem version: 1.28.0

What did you do?

I tried to add rudimentary support for AnyCable to Motion using channel state variables ( Please don't judge my code :see_no_evil: ).

What did you expect to happen?

I expected the state variable that I assigned to in subscribe to be available in actions (specifically process_motion) and unsubscribe.

What actually happened?

The channel state variable always appears to be nil. I've tried manually assigning in pry too, and I see the same issue.

I would include a reproduction script, but I'm unable to get the template working with AnyCable 1.0 (which I believe that I need for channel state variables).

Any help or pointers that you are able to provide would be appreciated. :smile:

palkan commented 4 years ago

I would include a reproduction script, but I'm unable to get the template working with AnyCable 1.0

I've upgraded the template, should work now.

Will be glad to help, let's try to reproduce it first)

alecdotninja commented 4 years ago

Thank you, @palkan !

Sorry for the delay here. I'm sure this is a silly error on my end, but I'm having some trouble getting my reproduction script to work:

# frozen_string_literal: true

require "bundler/inline"

gemfile(true) do
  source "https://rubygems.org"

  # For AnyCable <1.0, use ~> 0.8
  gem "anyt", ">= 1.0.0"
end

# Use HTTP adapter by default
ENV["ANYCABLE_BROADCAST_ADAPTER"] = "http"

require "anyt"
require "anyt/cli"

# Test scenario
feature "Channel State" do
  channel do
    state_attr_accessor :some_state

    def subscribed
      self.some_state = "a value"
    end

    def an_action(_data)
      transmit({ some_state: some_state })
    end
  end

  let(:identifier) { { channel: channel }.to_json }

  before do
    client.send(identifier: identifier, command: "subscribe")

    ack = {
      "identifier" => identifier,
      "type" => "confirm_subscription"
    }

    assert_equal ack, client.receive
  end

  scenario "can be set from `subscribed` and accessed within `an_action`" do
    client.send(identifier: identifier, command: "message", data: { action: "an_action" })

    msg = {
      "identifier" => identifier,
      "message" => {
        "some_state" => "a value"
      }
    }

    assert_equal msg, client.receive
  end
end

ARGV.clear

# WebSocket server url
ARGV << "--target-url=ws://localhost:8080/cable"
# Command to launch WebSocket server.
# Comment this line if you want to run WebSocket server manually
ARGV << "--command=anycable-go"
# Run only the scenarios specified in this file
ARGV << "--only=bug_report_template"

Anyt::Cli.run

Is client.send(identifier: identifier, command: "message", data: { action: "an_action" }) the correct way to call an_action here?

palkan commented 4 years ago

Data should also be JSON-encoded: data: { action: "an_action" }.to_json. See https://github.com/anycable/anyt/blob/cb983635e1f10e9a3e6c3ca46b34fb32ef555c68/lib/anyt/tests/subscriptions/perform_test.rb#L32-L36

alecdotninja commented 4 years ago

Thank you for all of your help! I think I have finally succeeded in reproducing the problem that I am seeing:

# frozen_string_literal: true

require "bundler/inline"

# This reproduction script is based on `anyt` gem
# (https://github.com/anycable/anyt).
#
# See more test examples here:
# https://github.com/anycable/anyt/tree/master/lib/anyt/tests

gemfile(true) do
  source "https://rubygems.org"

  # For AnyCable <1.0, use ~> 0.8
  gem "anyt", ">= 1.0.0"
end

# Use HTTP adapter by default
ENV["ANYCABLE_BROADCAST_ADAPTER"] = "http"

require "anyt"
require "anyt/cli"

# Test scenario
feature "Channel State" do
  channel do
    state_attr_accessor :some_state

    def subscribed
      self.some_state = "a value"
    end

    def an_action(_data)
      transmit({ some_state: some_state })
    end
  end

  let(:identifier) { { channel: channel }.to_json }

  before do
    client.send(identifier: identifier, command: "subscribe")

    ack = {
      "identifier" => identifier,
      "type" => "confirm_subscription"
    }

    assert_equal ack, client.receive
  end

  let(:data) { { action: "an_action" }.to_json }

  scenario "can be set from `subscribed` and accessed within `an_action`" do
    client.send(identifier: identifier, command: "message", data: data)

    msg = {
      "identifier" => identifier,
      "message" => {
        "some_state" => "a value"
      }
    }

    assert_equal msg, client.receive
  end
end

ARGV.clear

# WebSocket server url
ARGV << "--target-url=ws://localhost:8080/cable"
# Command to launch WebSocket server.
# Comment this line if you want to run WebSocket server manually
ARGV << "--command=anycable-go"
# Run only the scenarios specified in this file
ARGV << "--only=bug_report_template"

Anyt::Cli.run

Even though I previously set a channel state variable in subscribed, I still see it as nil in an_action:

Anyt::TestChannels::ChannelStateChannel#an_action
  can be set from `subscribed` and accessed within `an_action`    FAIL (0.55s)
Minitest::Assertion:         --- expected
        +++ actual
        @@ -1 +1 @@
        -{"identifier"=>"{\"channel\":\"Anyt::TestChannels::ChannelStateChannel\"}", "message"=>{"some_state"=>"a value"}}
        +{"identifier"=>"{\"channel\":\"Anyt::TestChannels::ChannelStateChannel\"}", "message"=>{"some_state"=>nil}}

Am I misunderstanding something?

palkan commented 4 years ago

Thank you! Looks good.

And what's more interesting, it passes for me:

$ ruby tmp/issue.rb       

Starting AnyT v1.0.1 (pid: 13028)
Skipping tests: core/ping, core/welcome, features/channel_state, features/remote_disconnect, features/server_restart, request/channel, request/connection, request/disconnect_reasons, request/disconnection, streams/broadcast, streams/multiple_clients, streams/multiple, streams/single, streams/stop, subscriptions/ack, subscriptions/params, subscriptions/perform, subscriptions/transmissions
RPC server is starting...
RPC server is listening on 127.0.0.1:50051
Started with run options --seed 46547

Channel State
Anyt::TestChannels::ChannelStateChannel is transmitting the subscription confirmation
Anyt::TestChannels::ChannelStateChannel#an_action
  can be set from `subscribed` and accessed within `an_action`    PASS (0.53s)

Finished in 0.53439s
1 tests, 2 assertions, 0 failures, 0 errors, 0 skips

I have:

alecdotninja commented 4 years ago

Thank you for your help, @palkan! I had been playing with anycable over the past couple of weeks and it seems I still had anycable-go 1.0.0-preview1 installed. Downloading the latest binary fixed the issue for me. :tada:

Sorry to waste your time with issues related to my setup. :see_no_evil: