litecable 0.4.1 → 0.7.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (61) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +34 -1
  3. data/LICENSE.txt +1 -1
  4. data/README.md +30 -28
  5. data/lib/lite_cable.rb +23 -2
  6. data/lib/lite_cable/anycable.rb +23 -14
  7. data/lib/lite_cable/broadcast_adapters.rb +35 -0
  8. data/lib/lite_cable/broadcast_adapters/any_cable.rb +11 -0
  9. data/lib/lite_cable/broadcast_adapters/base.rb +17 -0
  10. data/lib/lite_cable/broadcast_adapters/memory.rb +11 -0
  11. data/lib/lite_cable/channel.rb +1 -0
  12. data/lib/lite_cable/channel/base.rb +4 -2
  13. data/lib/lite_cable/channel/registry.rb +5 -0
  14. data/lib/lite_cable/channel/streams.rb +1 -2
  15. data/lib/lite_cable/coders.rb +1 -0
  16. data/lib/lite_cable/coders/json.rb +1 -0
  17. data/lib/lite_cable/coders/raw.rb +2 -1
  18. data/lib/lite_cable/config.rb +8 -6
  19. data/lib/lite_cable/connection.rb +1 -0
  20. data/lib/lite_cable/connection/authorization.rb +1 -0
  21. data/lib/lite_cable/connection/base.rb +2 -2
  22. data/lib/lite_cable/connection/identification.rb +3 -0
  23. data/lib/lite_cable/connection/streams.rb +1 -0
  24. data/lib/lite_cable/connection/subscriptions.rb +7 -2
  25. data/lib/lite_cable/internal.rb +1 -0
  26. data/lib/lite_cable/logging.rb +4 -2
  27. data/lib/lite_cable/server.rb +1 -6
  28. data/lib/lite_cable/server/client_socket.rb +1 -0
  29. data/lib/lite_cable/server/client_socket/base.rb +17 -21
  30. data/lib/lite_cable/server/client_socket/subscriptions.rb +3 -2
  31. data/lib/lite_cable/server/heart_beat.rb +4 -1
  32. data/lib/lite_cable/server/middleware.rb +7 -6
  33. data/lib/lite_cable/server/subscribers_map.rb +3 -0
  34. data/lib/lite_cable/version.rb +2 -1
  35. data/lib/litecable.rb +1 -0
  36. metadata +31 -77
  37. data/.gitignore +0 -40
  38. data/.rubocop.yml +0 -63
  39. data/.travis.yml +0 -7
  40. data/Gemfile +0 -4
  41. data/Rakefile +0 -6
  42. data/bin/console +0 -14
  43. data/bin/setup +0 -8
  44. data/circle.yml +0 -8
  45. data/examples/sinatra/Gemfile +0 -16
  46. data/examples/sinatra/Procfile +0 -3
  47. data/examples/sinatra/README.md +0 -33
  48. data/examples/sinatra/anycable +0 -18
  49. data/examples/sinatra/app.rb +0 -52
  50. data/examples/sinatra/assets/app.css +0 -169
  51. data/examples/sinatra/assets/cable.js +0 -584
  52. data/examples/sinatra/assets/reset.css +0 -223
  53. data/examples/sinatra/bin/anycable-go +0 -0
  54. data/examples/sinatra/chat.rb +0 -39
  55. data/examples/sinatra/config.ru +0 -28
  56. data/examples/sinatra/views/index.slim +0 -8
  57. data/examples/sinatra/views/layout.slim +0 -15
  58. data/examples/sinatra/views/login.slim +0 -8
  59. data/examples/sinatra/views/resetcss.slim +0 -224
  60. data/examples/sinatra/views/room.slim +0 -68
  61. data/litecable.gemspec +0 -33
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: a4cd86a9f6cb1636c77ec87485b61050eaffade5
4
- data.tar.gz: d602270a3b4e9f8e57744a226219743a6e71c510
2
+ SHA256:
3
+ metadata.gz: 94ece0fdf218bcdeefe8b375d73114c28bd869c5d232d1c38dd91f0e880c1e57
4
+ data.tar.gz: e53c002d7179a52793a1382dd88dbb782d43319801e0f5ff51d36dd32237c005
5
5
  SHA512:
6
- metadata.gz: 5959fb92c1c91a96f94f06c94af4a6ee27f6958c9b4690a4bb28091a37c5ba464534e5d5f1480c7d80e68694fbf112db3f6803d5a514ad867b98cbccbc1ba779
7
- data.tar.gz: 7d5aec25eaf3fadc7a210b10bcd1d6797e0329d58dcf80ee1dbf552d85aa8a4f2c01be8b3699b4cc3e4252c2bf77915b847936169cf13401695667192916488c
6
+ metadata.gz: 1867f388377ea2f29c63cea936e255dcea9b7011ce83a45f93965ee98507c00e063360d62fb436a80a767c52b23f00ff853e92dbb93421ab9b0f007243cbc1d7
7
+ data.tar.gz: 9d344ce7aa7f3f0c07dc8c6be396cd51ae18758ffd5ad3e07ff156704fe87ee4ab0d54c67f0e619b30cb5344635f39860df0919b6a9587230ae87aa7d226d0c7
@@ -1,8 +1,41 @@
1
1
  # Change log
2
2
 
3
+ ## master (unreleased)
4
+
5
+ ## 0.7.1 (2021-01-06)
6
+
7
+ - Fix handling client disconnection during socket write. ([@palkan][])
8
+
9
+ ## 0.7.0 (2020-01-07)
10
+
11
+ - Refactor AnyCable integration ([@palkan][])
12
+
13
+ Now you only need to set AnyCable broadcast adapter:
14
+
15
+ ```ruby
16
+ LiteCable.broadcast_adapter = :any_cable
17
+ ```
18
+
19
+ ```sh
20
+ # or via env/config
21
+ LITECABLE_BROADCAST_ADAPTER=any_cable ruby my_app.rb
22
+ ```
23
+
24
+ - Adapterize broadcast adapters ([@palkan][])
25
+
26
+ - Drop Ruby 2.4 support ([palkan][])
27
+
28
+ ## 0.6.0 (2019-04-12) 🚀
29
+
30
+ - Drop Ruby 2.3 support ([@palkan][])
31
+
32
+ ## 0.5.0 (2017-12-20)
33
+
34
+ - Upgrade for AnyCable 0.5.0 ([@palkan][])
35
+
3
36
  ## 0.4.1 (2017-02-04)
4
37
 
5
- - Use `websocket-ruby` with subprotocols support ([@palkan][])
38
+ - Use `websocket-ruby` with sub-protocols support ([@palkan][])
6
39
 
7
40
  ## 0.4.0 (2017-01-29)
8
41
 
@@ -1,6 +1,6 @@
1
1
  The MIT License (MIT)
2
2
 
3
- Copyright (c) 2017 palkan
3
+ Copyright (c) 2017-2020 palkan
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
data/README.md CHANGED
@@ -1,4 +1,5 @@
1
- [![Gem Version](https://badge.fury.io/rb/litecable.svg)](https://rubygems.org/gems/litecable) [![Build Status](https://travis-ci.org/palkan/litecable.svg?branch=master)](https://travis-ci.org/palkan/litecable) [![Circle CI](https://circleci.com/gh/palkan/litecable/tree/master.svg?style=svg)](https://circleci.com/gh/palkan/litecable/tree/master)
1
+ [![Gem Version](https://badge.fury.io/rb/litecable.svg)](https://rubygems.org/gems/litecable)
2
+ [![Build](https://github.com/palkan/litecable/workflows/Build/badge.svg)](https://github.com/palkan/litecable/actions)
2
3
 
3
4
  # Lite Cable
4
5
 
@@ -13,27 +14,23 @@ Compatible with [AnyCable](http://anycable.io) (for production usage).
13
14
 
14
15
  ## Examples
15
16
 
16
- - [Sinatra Lite Cable Chat](https://github.com/palkan/litecable/tree/master/examples/sinatra)
17
+ - [Sinatra LiteCable Chat](https://github.com/palkan/litecable/tree/master/examples/sinatra)
18
+
19
+ - [Connecting LiteCable to Hanami](http://gabrielmalakias.com.br/ruby/hanami/iot/2017/05/26/websockets-connecting-litecable-to-hanami.html) by [@GabrielMalakias](https://github.com/GabrielMalakias)
17
20
 
18
21
  ## Installation
19
22
 
20
23
  Add this line to your application's Gemfile:
21
24
 
22
25
  ```ruby
23
- gem 'litecable'
26
+ gem "litecable"
24
27
  ```
25
28
 
26
- And then execute:
27
-
28
- $ bundle
29
-
30
- Or install it yourself as:
31
-
32
- $ gem install litecable
29
+ And run `bundle install`.
33
30
 
34
31
  ## Usage
35
32
 
36
- Please, checkout [Action Cable guides](http://guides.rubyonrails.org/action_cable_overview.html) for general information. Lite Cable aims to be compatible with Action Cable as much as possible without the loss of simplicity and _ligthness_.
33
+ Please, checkout [Action Cable guides](http://guides.rubyonrails.org/action_cable_overview.html) for general information. Lite Cable aims to be compatible with Action Cable as much as possible without the loss of simplicity and _lightness_.
37
34
 
38
35
  You can use Action Cable javascript client without any change (precompiled version can be found [here](https://github.com/palkan/litecable/tree/master/examples/sinatra/assets/cable.js)).
39
36
 
@@ -71,36 +68,42 @@ To use Lite Cable server:
71
68
 
72
69
  ```ruby
73
70
  Rack::Builder.new do
74
- map '/cable' do
71
+ map "/cable" do
75
72
  # You have to specify your app's connection class
76
73
  use LiteCable::Server::Middleware, connection_class: App::Connection
77
- run proc { |_| [200, { 'Content-Type' => 'text/plain' }, ['OK']] }
74
+ run proc { |_| [200, {"Content-Type" => "text/plain"}, ["OK"]] }
78
75
  end
79
76
  end
80
77
  ```
81
78
 
82
79
  ### Using with AnyCable
83
80
 
84
- Lite Cable is AnyCable-compatible out-of-the-box. You should write a simple server script:
81
+ Lite Cable is AnyCable-compatible out-of-the-box:
82
+
83
+ - Set broadcast adapter to AnyCable:
85
84
 
86
85
  ```ruby
87
- #!/usr/bin/env ruby
86
+ LiteCable.broadcast_adapter = :any_cable
87
+ ```
88
88
 
89
- require "my_app"
90
- require "rack"
91
- require "anycable"
89
+ You can also do this via configuration, e.g., env var (`LITECABLE_BROADCAST_ADAPTER=any_cable`) or `broadcast_adapter: any_cable` in a YAML config.
92
90
 
93
- # Turn AnyCable compatibility mode
94
- LiteCable.anycable!
91
+ - Configure connection factory:
95
92
 
96
- Anycable.configure do |config|
97
- config.connection_factory = MyApp::Connection
98
- end
93
+ ```ruby
94
+ AnyCable.connection_factory = MyApp::Connection
95
+ ```
99
96
 
100
- Anycable::Server.start
97
+ Then run AnyCable along with the app:
98
+
99
+ ```sh
100
+ bundle exec anycable
101
+
102
+ # add -r option to load the app if it's not ./config/anycable.rb or ./config/environment.rb
103
+ bundle exec anycable -r ./my_app.rb
101
104
  ```
102
105
 
103
- And then run this script along with your application. See [Sinatra example](https://github.com/palkan/litecable/tree/master/examples/sinatra) for more.
106
+ See [Sinatra example](https://github.com/palkan/litecable/tree/master/examples/sinatra) for more.
104
107
 
105
108
  ### Configuration
106
109
 
@@ -120,9 +123,8 @@ See [config](https://github.com/palkan/litecable/blob/master/lib/lite_cable/conf
120
123
 
121
124
  ## Contributing
122
125
 
123
- Bug reports and pull requests are welcome on GitHub at https://github.com/anycable/litecable.
126
+ Bug reports and pull requests are welcome on GitHub at [https://github.com/palkan/litecable](https://github.com/palkan/litecable).
124
127
 
125
128
  ## License
126
129
 
127
- The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
128
-
130
+ The gem is available as open source under the terms of the [MIT License](./LICENSE.txt).
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require "lite_cable/version"
3
4
  require "lite_cable/internal"
4
5
  require "lite_cable/logging"
@@ -14,6 +15,7 @@ module LiteCable
14
15
  require "lite_cable/channel"
15
16
  require "lite_cable/coders"
16
17
  require "lite_cable/config"
18
+ require "lite_cable/broadcast_adapters"
17
19
  require "lite_cable/anycable"
18
20
 
19
21
  class << self
@@ -22,8 +24,27 @@ module LiteCable
22
24
  end
23
25
 
24
26
  # Broadcast encoded message to the stream
25
- def broadcast(*args)
26
- LiteCable::Server.broadcast(*args)
27
+ def broadcast(stream, message, coder: LiteCable.config.coder)
28
+ broadcast_adapter.broadcast(stream, message, coder: coder)
29
+ end
30
+
31
+ def broadcast_adapter
32
+ return @broadcast_adapter if defined?(@broadcast_adapter)
33
+ self.broadcast_adapter = LiteCable.config.broadcast_adapter.to_sym
34
+ @broadcast_adapter
35
+ end
36
+
37
+ def broadcast_adapter=(adapter)
38
+ if adapter.is_a?(Symbol) || adapter.is_a?(Array)
39
+ adapter = BroadcastAdapters.lookup_adapter(adapter)
40
+ end
41
+
42
+ unless adapter.respond_to?(:broadcast)
43
+ raise ArgumentError, "BroadcastAdapter must implement #broadcast method. " \
44
+ "#{adapter.class} doesn't implement it."
45
+ end
46
+
47
+ @broadcast_adapter = adapter
27
48
  end
28
49
  end
29
50
  end
@@ -1,21 +1,15 @@
1
1
  # frozen_string_literal: true
2
- module LiteCable
2
+
3
+ module LiteCable # :nodoc:
3
4
  # AnyCable extensions
4
5
  module AnyCable
5
- module Broadcasting # :nodoc:
6
- def broadcast(stream, message, coder: nil)
7
- coder ||= LiteCable.config.coder
8
- Anycable.broadcast stream, coder.encode(message)
9
- end
10
- end
11
-
12
6
  module Connection # :nodoc:
13
7
  def self.extended(base)
14
8
  base.prepend InstanceMethods
15
9
  end
16
10
 
17
- def create(socket, **options)
18
- new(socket, **options)
11
+ def call(socket, **options)
12
+ new(socket, options)
19
13
  end
20
14
 
21
15
  module InstanceMethods # :nodoc:
@@ -29,6 +23,7 @@ module LiteCable
29
23
  @request ||= Rack::Request.new(socket.env)
30
24
  end
31
25
 
26
+ # rubocop: disable Metrics/MethodLength
32
27
  def handle_channel_command(identifier, command, data)
33
28
  channel = subscriptions.add(identifier, false)
34
29
  case command
@@ -44,19 +39,33 @@ module LiteCable
44
39
  false
45
40
  end
46
41
  rescue LiteCable::Connection::Subscriptions::Error,
47
- LiteCable::Channel::Error,
48
- LiteCable::Channel::Registry::Error => e
42
+ LiteCable::Channel::Error,
43
+ LiteCable::Channel::Registry::Error => e
49
44
  log(:error, log_fmt("Connection command failed: #{e}"))
50
45
  close
51
46
  false
52
47
  end
48
+ # rubocop: enable Metrics/MethodLength
53
49
  end
54
50
  end
55
51
  end
56
52
 
57
- # Patch Lite Cable with AnyCable functionality
53
+ # Patch Lite Cable with AnyCable functionality
58
54
  def self.anycable!
59
55
  LiteCable::Connection::Base.extend LiteCable::AnyCable::Connection
60
- LiteCable.singleton_class.prepend LiteCable::AnyCable::Broadcasting
56
+ end
57
+ end
58
+
59
+ if defined?(AnyCable)
60
+ AnyCable.configure_server do
61
+ # Make sure broadcast adapter is valid
62
+ require "lite_cable/broadcast_adapters/any_cable"
63
+ unless LiteCable::BroadcastAdapters::AnyCable === LiteCable.broadcast_adapter
64
+ raise "You should use :any_cable broadcast adapter (current: #{LiteCable.broadcast_adapter.class}). " \
65
+ "Set it via LITECABLE_BROADCAST_ADAPTER=any_cable or in the code/YML."
66
+ end
67
+
68
+ # Turn AnyCable compatibility mode for anycable RPC server automatically
69
+ LiteCable.anycable!
61
70
  end
62
71
  end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "lite_cable/broadcast_adapters/base"
4
+
5
+ module LiteCable
6
+ module BroadcastAdapters # :nodoc:
7
+ module_function
8
+
9
+ # rubocop: disable Metrics/AbcSize, Metrics/MethodLength
10
+ def lookup_adapter(args)
11
+ adapter, options = Array(args)
12
+ path_to_adapter = "lite_cable/broadcast_adapters/#{adapter}"
13
+ adapter_class_name = adapter.to_s.split("_").map(&:capitalize).join
14
+
15
+ unless BroadcastAdapters.const_defined?(adapter_class_name, false)
16
+ begin
17
+ require path_to_adapter
18
+ rescue LoadError => e
19
+ # We couldn't require the adapter itself.
20
+ if e.path == path_to_adapter
21
+ raise e.class, "Couldn't load the '#{adapter}' broadcast adapter for LiteCable",
22
+ e.backtrace
23
+ # Bubbled up from the adapter require.
24
+ else
25
+ raise e.class, "Error loading the '#{adapter}' broadcast adapter for LiteCable",
26
+ e.backtrace
27
+ end
28
+ end
29
+ end
30
+
31
+ BroadcastAdapters.const_get(adapter_class_name, false).new(**(options || {}))
32
+ end
33
+ # rubocop: enable Metrics/AbcSize, Metrics/MethodLength
34
+ end
35
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module LiteCable
4
+ module BroadcastAdapters
5
+ class AnyCable < Base
6
+ def broadcast(stream, message, coder:)
7
+ ::AnyCable.broadcast stream, coder.encode(message)
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ # frozen_string_literal: true
4
+
5
+ module LiteCable
6
+ module BroadcastAdapters
7
+ class Base
8
+ def initialize(**options)
9
+ @options = options
10
+ end
11
+
12
+ private
13
+
14
+ attr_reader :options
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module LiteCable
4
+ module BroadcastAdapters
5
+ class Memory < Base
6
+ def broadcast(stream, message, coder:)
7
+ Server.subscribers_map.broadcast stream, message, coder
8
+ end
9
+ end
10
+ end
11
+ end
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module LiteCable
3
4
  module Channel # :nodoc:
4
5
  require "lite_cable/channel/registry"
@@ -1,9 +1,11 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module LiteCable
3
- # rubocop:disable Metrics/LineLength
4
4
  module Channel
5
5
  class Error < StandardError; end
6
+
6
7
  class RejectedError < Error; end
8
+
7
9
  class UnproccessableActionError < Error; end
8
10
 
9
11
  # The channel provides the basic structure of grouping behavior into logical units when communicating over the connection.
@@ -62,7 +64,6 @@ module LiteCable
62
64
  # client-side, the <tt>Channel#rejected</tt> callback will get invoked when
63
65
  # the server rejects the subscription request.
64
66
  class Base
65
- # rubocop:enable Metrics/LineLength
66
67
  class << self
67
68
  # A set of method names that should be considered actions.
68
69
  # This includes all public instance methods on a channel except from Channel::Base methods.
@@ -128,6 +129,7 @@ module LiteCable
128
129
  action = extract_action(data)
129
130
 
130
131
  raise UnproccessableActionError unless processable_action?(action)
132
+
131
133
  log(:debug) { log_fmt("Perform action #{action}(#{data})") }
132
134
  dispatch_action(action, data)
133
135
  end
@@ -1,15 +1,19 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module LiteCable
3
4
  module Channel
4
5
  # Stores channels identifiers and corresponding classes.
5
6
  module Registry
6
7
  class Error < StandardError; end
8
+
7
9
  class AlreadyRegisteredError < Error; end
10
+
8
11
  class UnknownChannelError < Error; end
9
12
 
10
13
  class << self
11
14
  def add(id, channel_class)
12
15
  raise AlreadyRegisteredError if find(id)
16
+
13
17
  channels[id] = channel_class
14
18
  end
15
19
 
@@ -20,6 +24,7 @@ module LiteCable
20
24
  def find!(id)
21
25
  channel_class = find(id)
22
26
  raise UnknownChannelError unless channel_class
27
+
23
28
  channel_class
24
29
  end
25
30
 
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module LiteCable
3
- # rubocop:disable Metrics/LineLength
4
4
  module Channel
5
5
  # Streams allow channels to route broadcastings to the subscriber. A broadcasting is a pubsub queue where any data
6
6
  # placed into it is automatically sent to the clients that are connected at that time.
@@ -28,7 +28,6 @@ module LiteCable
28
28
  #
29
29
  # You can stop streaming from all broadcasts by calling #stop_all_streams or use #stop_from to stop streaming broadcasts from the specified stream.
30
30
  module Streams
31
- # rubocop:enable Metrics/LineLength
32
31
  def handle_unsubscribe
33
32
  stop_all_streams
34
33
  super