anycable-rails 1.0.0.preview2 → 1.0.0.rc1

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.
Files changed (32) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +14 -125
  3. data/README.md +14 -34
  4. data/lib/anycable/rails.rb +36 -2
  5. data/lib/anycable/rails/actioncable/channel.rb +4 -0
  6. data/lib/anycable/rails/actioncable/connection.rb +24 -35
  7. data/lib/anycable/rails/actioncable/connection/serializable_identification.rb +42 -0
  8. data/lib/anycable/rails/actioncable/remote_connections.rb +11 -0
  9. data/lib/anycable/rails/channel_state.rb +48 -0
  10. data/lib/anycable/rails/compatibility.rb +4 -7
  11. data/lib/anycable/rails/compatibility/rubocop.rb +0 -1
  12. data/lib/anycable/rails/compatibility/rubocop/config/default.yml +3 -0
  13. data/lib/anycable/rails/compatibility/rubocop/cops/anycable/instance_vars.rb +1 -1
  14. data/lib/anycable/rails/compatibility/rubocop/cops/anycable/stream_from.rb +4 -4
  15. data/lib/anycable/rails/railtie.rb +10 -10
  16. data/lib/anycable/rails/refinements/subscriptions.rb +5 -0
  17. data/lib/anycable/rails/session_proxy.rb +19 -2
  18. data/lib/anycable/rails/version.rb +1 -1
  19. data/lib/generators/anycable/download/USAGE +14 -0
  20. data/lib/generators/anycable/download/download_generator.rb +77 -0
  21. data/lib/generators/anycable/setup/USAGE +2 -0
  22. data/lib/{rails/generators → generators}/anycable/setup/setup_generator.rb +67 -89
  23. data/lib/{rails/generators → generators}/anycable/setup/templates/Procfile.dev +0 -0
  24. data/lib/{rails/generators → generators}/anycable/setup/templates/config/anycable.yml.tt +12 -1
  25. data/lib/generators/anycable/setup/templates/config/cable.yml.tt +11 -0
  26. data/lib/{rails/generators/anycable/setup/templates/config/initializers/anycable.rb → generators/anycable/setup/templates/config/initializers/anycable.rb.tt} +1 -1
  27. data/lib/generators/anycable/with_os_helpers.rb +55 -0
  28. metadata +23 -47
  29. data/lib/anycable/rails/compatibility/rubocop/cops/anycable/remote_disconnect.rb +0 -31
  30. data/lib/rails/generators/anycable/setup/templates/Procfile +0 -2
  31. data/lib/rails/generators/anycable/setup/templates/bin/heroku-web +0 -7
  32. data/lib/rails/generators/anycable/setup/templates/config/cable.yml.tt +0 -11
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 93c88ec3ccf3eb9d145474e43aa8a851e32768de9b7c8ce1e6eb44cc0b24b0e9
4
- data.tar.gz: b12bc69aa43afc035038ee85c80bce57b9beabe36d9d0b93f261125f3af32876
3
+ metadata.gz: 36b6e47a498bfc2d1156e4635a5c30f60258c925142c59ad837c1b6708f1ff73
4
+ data.tar.gz: f27c94856df0db9d178537e8ac75598f8a355b338e725a80a5cb06a6e0ac610c
5
5
  SHA512:
6
- metadata.gz: 387d765e0d5f15c38d211e206eccc04982c4d20c4855539de49caefa58caffd0dd7a949f73817d576fc19b97a1e6359d0dd194520a3e9538b18aee987a5aa99e
7
- data.tar.gz: c75876c69f2655296cefd54b3ac9cba5fdefcdabbea206c526d9ddb502fcb61095e2855582d5ca17884fdadf70e6e4406a43b5e21470b7ade8b6027064903c1e
6
+ metadata.gz: 28b72e3f6a9950d3a95eb8d4f1c6a59f24a704ff63ac50384ffb2d22aa97c9f6b6e1f0c882a89cc6a0fd9ac22ea553a381514d56885f6f1cce41e7146153ab24
7
+ data.tar.gz: ac7c402d2b57a332ced9372a4546709d6b41a0fb082c823d292b87c8b11e4bf741047cbc10258f68acd228712f0ca974b6723246bd3ddc689920f656b1488974
@@ -1,6 +1,18 @@
1
1
  # Change log
2
2
 
3
- ## 🚧 1.0.0 (_coming soon_)
3
+ ## 1.0.0.rc1 (2020-06-10)
4
+
5
+ - Add `state_attr_accessor` for channels. ([@palkan][])
6
+
7
+ Just like `attr_accessor` but "persists" the state between RPC calls.
8
+
9
+ - Add `Channel#stop_stream_from` support. ([@palkan][])
10
+
11
+ - Add `RemoteConnections` support. ([@palkan][])
12
+
13
+ - Add `AnyCable::Rails.enabled?` method which returns true if Action Cable uses AnyCable adapter. ([@palkan][])
14
+
15
+ - Add `anycable:download` generator to download `anycable-go` binary. ([@palkan][])
4
16
 
5
17
  - **Ruby 2.5+ is required**. ([@palkan][])
6
18
 
@@ -23,130 +35,7 @@ You must turn this feature on by setting `persistent_session_enabled: true` in t
23
35
 
24
36
  - Add ability to develop the gem with Docker. ([@bibendi][])
25
37
 
26
- ## 0.6.4 (2019-06-26) 👶
27
-
28
- - Fix Compatibility bug when using with AnyCable. ([@palkan][])
29
-
30
- Compatibility patching (with `prepend` + `super`) conflicted with
31
- the `ActionCable::Channel::Base` patching in the core functionality (which uses `alias`).
32
-
33
- See [#95](https://github.com/anycable/anycable-rails/issues/95).
34
-
35
- ## 0.6.3 (2019-03-26)
36
-
37
- - Fix connection factory reloading for development sake. ([@sponomarev][])
38
-
39
- - Add `:anycable` subscription adapter alias. ([@sponomarev][])
40
-
41
- - Don't set AnyCable connection factory for incompatible adapter ([@sponomarev][])
42
-
43
- `anycable` server won't start with unpatched vanilla `ApplicationCable::Connection`.
44
-
45
- Motivation in [#74](https://github.com/anycable/anycable-rails/issues/74).
46
-
47
- - Fix instance detection inside complex cases in compatibility cops ([@sponomarev][])
48
-
49
- ## 0.6.2 (2019-01-10)
50
-
51
- - Fixed `anycable` 0.6.1 compatibility. ([@palkan][])
52
-
53
- - Broadcast logs to STDOUT in development only when server is running. ([@palkan][])
54
-
55
- Fixes #59.
56
-
57
- ## 0.6.1 (2018-11-15)
58
-
59
- - Fix regression introduced in [e64a366e](https://github.com/anycable/anycable-rails/commit/e64a366ea21293925e0c5c0b8e6595d65d5d0981#diff-fd0e56a6e825002eac978507c3581af7R14) ([@palkan][])
60
-
61
- `Connection` patch could be loaded after `identify_by` is called, thus breaking
62
- identifiers.
63
-
64
- ## 0.6.0 (2018-11-15)
65
-
66
- **NOTE**: this version has been yanked from RubyGems due to the regression bug. Use 0.6.1.
67
-
68
- - [PR #56](https://github.com/anycable/anycable-rails/pull/56) Request verification based on ActionCable config. ([@DmitryTsepelev][])
69
-
70
- - Add WS server session ID to log tags if present. ([@palkan][])
71
-
72
- - Support tagged logging. ([@palkan][])
73
-
74
- - Action Cable monkey-patches are only loaded in the context of AnyCable CLI. ([@palkan][])
75
-
76
- No need to think about `requie` and `group` for `anycable-rails`, just add it to Gemfile.
77
-
78
- - Add `:any_cable` subscription adapter for Action Cable. ([@palkan][])
79
-
80
- Use `:any_cable` adapter for Action Cable to broadcast data to AnyCable.
81
-
82
- No more `pubsub` monkey-patches 🎉.
83
-
84
- - Added Rails executor/reloader support. ([@palkan][])
85
-
86
- - **[Breaking]** No more generators. ([@palkan][])
87
-
88
- No need to generate AnyCable runner script since `anycable` gem ships with
89
- the CLI.
90
-
91
- - Add dynamic (`AnyCable::CompatibilityError`) compatibility checks. ([@DmitryTsepelev][])
92
-
93
- - Added static (RuboCop) compatibility checks. ([@DmitryTsepelev][])
94
-
95
- See https://github.com/anycable/anycable-rails/issues/52
96
-
97
- ## 0.5.4 (2018-06-13)
98
-
99
- - Fix duplicate logs in development. ([@palkan][])
100
-
101
- Fixes https://github.com/anycable/anycable_demo/issues/5.
102
-
103
- ## 0.5.3
104
-
105
- - Fix return value of `Connection#handle_close`. ([@palkan][])
106
-
107
- Should always be `true`, we do not expect a failure here.
108
-
109
- ## 0.5.2
110
-
111
- - Add config/anycable.yml to Rails generator. ([@alekseyl][])
112
-
113
- ## 0.5.1
114
-
115
- - Improve Rails integration. ([@palkan][])
116
-
117
- Log to STDOUT in development.
118
- Make order of initializers more deterministic.
119
- Show warning if AnyCable is loaded after application initialization.
120
-
121
- ## 0.5.0
122
-
123
- - [#17](https://github.com/anycable/anycable-rails/issues/17) Refactor logging. ([@palkan][])
124
-
125
- Use Rails logger everywhere.
126
-
127
- Add access logs ([anycable/anycable#20](https://github.com/anycable/anycable/issues/20)).
128
-
129
- ## 0.4.7
130
-
131
- - Minor fixes. ([@palkan][])
132
-
133
- ## 0.4.6
134
-
135
- - Disable mounting default Action Cable server when AnyCable is loaded. ([@palkan][])
136
-
137
- ## 0.4.5
138
-
139
- - Handle tagged logger. ([@palkan][])
140
-
141
- Ignore tagged logger features ('cause we do not have _persistent_ logger).
142
-
143
- ## 0.4.4
144
-
145
- - Fix bug with ActiveRecord connections (https://github.com/anycable/anycable/issues/9). ([@palkan][])
146
-
147
- ## 0.4.0
148
-
149
- - Initial version. ([@palkan][])
38
+ See [Changelog](https://github.com/anycable/anycable-rails/blob/0-6-stable/CHANGELOG.md) for versions <1.0.0.
150
39
 
151
40
  [@palkan]: https://github.com/palkan
152
41
  [@alekseyl]: https://github.com/alekseyl
data/README.md CHANGED
@@ -1,6 +1,8 @@
1
- [![GitPitch](https://gitpitch.com/assets/badge.svg)](https://gitpitch.com/anycable/anycable/master?grs=github) [![Gem Version](https://badge.fury.io/rb/anycable-rails.svg)](https://rubygems.org/gems/anycable-rails) [![Build Status](https://travis-ci.org/anycable/anycable-rails.svg?branch=master)](https://travis-ci.org/anycable/anycable-rails)
1
+ [![GitPitch](https://gitpitch.com/assets/badge.svg)](https://gitpitch.com/anycable/anycable/master?grs=github)
2
+ [![Gem Version](https://badge.fury.io/rb/anycable-rails.svg)](https://rubygems.org/gems/anycable-rails)
3
+ [![Build](https://github.com/anycable/anycable-rails/workflows/Build/badge.svg)](https://github.com/anycable/anycable-rails/actions)
2
4
  [![Gitter](https://img.shields.io/badge/gitter-join%20chat%20%E2%86%92-brightgreen.svg)](https://gitter.im/anycable/Lobby)
3
- [![Documentation](https://img.shields.io/badge/docs-link-brightgreen.svg)](https://docs.anycable.io/#/ruby/rails)
5
+ [![Documentation](https://img.shields.io/badge/docs-link-brightgreen.svg)](https://docs.anycable.io/v1/#/ruby/rails)
4
6
 
5
7
  # AnyCable Rails
6
8
 
@@ -10,10 +12,11 @@ With AnyCable you can use channels, client-side JS, broadcasting - (almost) all
10
12
 
11
13
  You can even use Action Cable in development and not be afraid of [compatibility issues](#compatibility).
12
14
 
13
- 💾 [Example Application](https://github.com/anycable/anycable_demo)
15
+ **Important** This is a readme for the upcoming v1.0 release. For v0.6.x see the readme from the [0-6-stable](https://github.com/anycable/anycable-rails/tree/0-6-stable) branch.
14
16
 
15
- 📑 [Documentation](https://docs.anycable.io).
17
+ <!-- 💾 [Example Application](https://github.com/anycable/anycable_demo) -->
16
18
 
19
+ 📑 [Documentation](https://docs.anycable.io/v1/#/ruby/rails).
17
20
 
18
21
  <a href="https://evilmartians.com/">
19
22
  <img src="https://evilmartians.com/badges/sponsored-by-evil-martians.svg" alt="Sponsored by Evil Martians" width="236" height="54"></a>
@@ -21,12 +24,8 @@ You can even use Action Cable in development and not be afraid of [compatibility
21
24
  ## Requirements
22
25
 
23
26
  - Ruby >= 2.5
24
- - Rails >= 5.0;
25
- - Redis (see [other options]() for broadcasting)
26
-
27
- ## How It Works?
28
-
29
- <img src="https://trello-attachments.s3.amazonaws.com/5781e0ed48e4679e302833d3/820x987/5b6a305417b04e20e75f49c5816e027c/Anycable_vs_ActionCable_copy.jpg" width="400" />
27
+ - Rails >= 5.2
28
+ - Redis (see [other options](https://github.com/anycable/anycable/issues/2) for broadcasting)
30
29
 
31
30
  ## Usage
32
31
 
@@ -39,14 +38,12 @@ gem "anycable-rails"
39
38
  gem "redis", ">= 4.0"
40
39
  ```
41
40
 
42
- (and don't forget to run `bundle install`).
43
-
44
41
  ### Interactive set up
45
42
 
46
43
  After the gem was installed, you can run an interactive wizard to configure your Rails application for using with AnyCable by running a generator:
47
44
 
48
45
  ```sh
49
- bin/rails g anycable:setup
46
+ bundle exec rails g anycable:setup
50
47
  ```
51
48
 
52
49
  ### Manual set up
@@ -86,38 +83,21 @@ $ bundle exec anycable
86
83
  $ RAILS_ENV=production bundle exec anycable
87
84
  ```
88
85
 
89
- And, finally, run AnyCable WebSocket server, e.g. [anycable-go](https://docs.anycable.io/#/anycable-go/getting_started):
86
+ And, finally, run AnyCable WebSocket server, e.g. [anycable-go](https://docs.anycable.io/v1/#/anycable-go/getting_started):
90
87
 
91
88
  ```sh
92
89
  anycable-go --host=localhost --port=3334
93
90
  ```
94
91
 
95
- See [documentation](https://docs.anycable.io/#/ruby/rails) for more information on AnyCable + Rails usage.
92
+ See [documentation](https://docs.anycable.io/v1/#/ruby/rails) for more information on AnyCable + Rails usage.
96
93
 
97
94
  ## Action Cable Compatibility
98
95
 
99
- See [documentation](https://docs.anycable.io/#/ruby/compatibility).
100
-
101
- ## Links
102
-
103
- - [AnyCable: Action Cable on steroids!](https://evilmartians.com/chronicles/anycable-actioncable-on-steroids)
104
-
105
- - [From Action to Any](https://medium.com/@leshchuk/from-action-to-any-1e8d863dd4cf) by [@alekseyl](https://github.com/alekseyl)
106
-
107
- ## Talks
108
-
109
- - One cable to rule them all, RubyKaigi 2018, [slides](https://speakerdeck.com/palkan/rubykaigi-2018-anycable-one-cable-to-rule-them-all) and [video](https://www.youtube.com/watch?v=jXCPuNICT8s) (EN)
110
-
111
- - Wroc_Love.rb 2018 [slides](https://speakerdeck.com/palkan/wroc-love-dot-rb-2018-cables-cables-cables) and [video](https://www.youtube.com/watch?v=AUxFFOehiy0) (EN)
112
-
113
- ## Compatible WebSocket servers
114
-
115
- - [AnyCable Go](https://github.com/anycable/anycable-go)
116
- - [ErlyCable](https://github.com/anycable/erlycable)
96
+ See [documentation](https://docs.anycable.io/v1/#/ruby/compatibility).
117
97
 
118
98
  ## Contributing
119
99
 
120
- Bug reports and pull requests are welcome on GitHub at https://github.com/anycable/anycable-rails.
100
+ Bug reports and pull requests are welcome on GitHub at [https://github.com/anycable/anycable-rails](https://github.com/anycable/anycable-rails).
121
101
 
122
102
  ## Development
123
103
 
@@ -5,6 +5,8 @@ require "anycable/rails/version"
5
5
  require "anycable/rails/config"
6
6
  require "anycable/rails/rack"
7
7
 
8
+ require "globalid"
9
+
8
10
  module AnyCable
9
11
  # Rails handler for AnyCable
10
12
  module Rails
@@ -12,8 +14,40 @@ module AnyCable
12
14
 
13
15
  ADAPTER_ALIASES = %w[any_cable anycable].freeze
14
16
 
15
- def self.compatible_adapter?(adapter)
16
- ADAPTER_ALIASES.include?(adapter)
17
+ class << self
18
+ def enabled?
19
+ adapter = ::ActionCable.server.config.cable&.fetch("adapter", nil)
20
+ compatible_adapter?(adapter)
21
+ end
22
+
23
+ def compatible_adapter?(adapter)
24
+ ADAPTER_ALIASES.include?(adapter)
25
+ end
26
+
27
+ # Serialize connection/channel state variable to string
28
+ # using GlobalID where possible or JSON (if json: true)
29
+ def serialize(obj, json: false)
30
+ obj.try(:to_gid_param) || (json ? obj.to_json : obj)
31
+ end
32
+
33
+ # Deserialize previously serialized value from string to
34
+ # Ruby object.
35
+ # If the resulting object is a Hash, make it indifferent
36
+ def deserialize(str, json: false)
37
+ str.yield_self do |val|
38
+ next unless val.is_a?(String)
39
+
40
+ gval = GlobalID::Locator.locate(val)
41
+ return gval if gval
42
+
43
+ next val unless json
44
+
45
+ JSON.parse(val)
46
+ end.yield_self do |val|
47
+ next val.with_indifferent_access if val.is_a?(Hash)
48
+ val
49
+ end
50
+ end
17
51
  end
18
52
  end
19
53
  end
@@ -28,6 +28,10 @@ module ActionCable
28
28
  connection.socket.subscribe identifier, broadcasting
29
29
  end
30
30
 
31
+ def stop_stream_from(broadcasting)
32
+ connection.socket.unsubscribe identifier, broadcasting
33
+ end
34
+
31
35
  def stop_all_streams
32
36
  connection.socket.unsubscribe_from_all identifier
33
37
  end
@@ -1,8 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "action_cable/connection"
4
+ require "anycable/rails/actioncable/connection/serializable_identification"
4
5
  require "anycable/rails/refinements/subscriptions"
5
6
  require "anycable/rails/actioncable/channel"
7
+ require "anycable/rails/actioncable/remote_connections"
6
8
  require "anycable/rails/session_proxy"
7
9
 
8
10
  module ActionCable
@@ -15,26 +17,26 @@ module ActionCable
15
17
 
16
18
  using AnyCable::Refinements::Subscriptions
17
19
 
20
+ include SerializableIdentification
21
+
18
22
  attr_reader :socket
19
23
 
20
24
  delegate :env, :session, to: :request
21
25
 
22
26
  class << self
23
27
  def call(socket, **options)
24
- new(socket, options)
28
+ new(socket, nil, options)
25
29
  end
30
+ end
26
31
 
27
- def identified_by(*identifiers)
28
- super
29
- Array(identifiers).each do |identifier|
30
- define_method(identifier) do
31
- instance_variable_get(:"@#{identifier}") || fetch_identifier(identifier)
32
- end
33
- end
32
+ def initialize(socket, env, identifiers: "{}", subscriptions: [])
33
+ if env
34
+ # If env is set, then somehow we're in the context of Action Cable
35
+ # Return and print a warning in #process
36
+ @request = ActionDispatch::Request.new(env)
37
+ return
34
38
  end
35
- end
36
39
 
37
- def initialize(socket, identifiers: "{}", subscriptions: [])
38
40
  @ids = ActiveSupport::JSON.decode(identifiers)
39
41
 
40
42
  @ltags = socket.cstate.read(LOG_TAGS_IDENTIFIER).yield_self do |raw_tags|
@@ -51,6 +53,18 @@ module ActionCable
51
53
  subscriptions.each { |id| @subscriptions.fetch(id) }
52
54
  end
53
55
 
56
+ def process
57
+ # Use Rails logger here to print to stdout in development
58
+ logger.error invalid_request_message
59
+ logger.info finished_request_message
60
+ [404, {"Content-Type" => "text/plain"}, ["Page not found"]]
61
+ end
62
+
63
+ def invalid_request_message
64
+ "You're trying to connect to Action Cable server while using AnyCable. " \
65
+ "See https://docs.anycable.io/v1/#/troubleshooting?id=server-raises-an-argumenterror-exception-when-client-tries-to-connect"
66
+ end
67
+
54
68
  def handle_open
55
69
  logger.info started_request_message if access_logs?
56
70
 
@@ -107,31 +121,6 @@ module ActionCable
107
121
  socket.transmit encode(cable_message)
108
122
  end
109
123
 
110
- # Generate identifiers info.
111
- # Converts GlobalID compatible vars to corresponding global IDs params.
112
- def identifiers_hash
113
- identifiers.each_with_object({}) do |id, acc|
114
- obj = instance_variable_get("@#{id}")
115
- next unless obj
116
-
117
- acc[id] = obj.try(:to_gid_param) || obj
118
- end.compact
119
- end
120
-
121
- def identifiers_json
122
- identifiers_hash.to_json
123
- end
124
-
125
- # Fetch identifier and deserialize if neccessary
126
- def fetch_identifier(name)
127
- @cached_ids[name] ||= @cached_ids.fetch(name) do
128
- val = ids[name.to_s]
129
- next val unless val.is_a?(String)
130
-
131
- GlobalID::Locator.locate(val) || val
132
- end
133
- end
134
-
135
124
  def logger
136
125
  @logger ||= TaggedLoggerProxy.new(AnyCable.logger, tags: ltags || [])
137
126
  end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActionCable
4
+ module Connection
5
+ module SerializableIdentification
6
+ extend ActiveSupport::Concern
7
+
8
+ class_methods do
9
+ def identified_by(*identifiers)
10
+ super
11
+ Array(identifiers).each do |identifier|
12
+ define_method(identifier) do
13
+ instance_variable_get(:"@#{identifier}") || fetch_identifier(identifier)
14
+ end
15
+ end
16
+ end
17
+ end
18
+
19
+ # Generate identifiers info.
20
+ # Converts GlobalID compatible vars to corresponding global IDs params.
21
+ def identifiers_hash
22
+ identifiers.each_with_object({}) do |id, acc|
23
+ obj = instance_variable_get("@#{id}")
24
+ next unless obj
25
+
26
+ acc[id] = AnyCable::Rails.serialize(obj)
27
+ end.compact
28
+ end
29
+
30
+ def identifiers_json
31
+ identifiers_hash.to_json
32
+ end
33
+
34
+ # Fetch identifier and deserialize if neccessary
35
+ def fetch_identifier(name)
36
+ @cached_ids[name] ||= @cached_ids.fetch(name) do
37
+ AnyCable::Rails.deserialize(ids[name.to_s])
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end