celluloid_pubsub 0.0.1
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 +7 -0
- data/.coveralls.yml +2 -0
- data/.gitignore +16 -0
- data/.rspec +1 -0
- data/.rubocop.yml +68 -0
- data/.travis.yml +12 -0
- data/CONTRIBUTING.md +44 -0
- data/Gemfile +3 -0
- data/Guardfile +12 -0
- data/LICENSE +20 -0
- data/README.rdoc +96 -0
- data/Rakefile +42 -0
- data/bin/appraisal +16 -0
- data/bin/autospec +16 -0
- data/bin/bundler +16 -0
- data/bin/cdiff +16 -0
- data/bin/coderay +16 -0
- data/bin/colortab +16 -0
- data/bin/coveralls +16 -0
- data/bin/decolor +16 -0
- data/bin/erubis +16 -0
- data/bin/guard +16 -0
- data/bin/htmldiff +16 -0
- data/bin/ldiff +16 -0
- data/bin/listen +16 -0
- data/bin/nokogiri +16 -0
- data/bin/phare +16 -0
- data/bin/pry +16 -0
- data/bin/rackup +16 -0
- data/bin/rails +16 -0
- data/bin/rake +16 -0
- data/bin/restclient +16 -0
- data/bin/rspec +16 -0
- data/bin/rubocop +16 -0
- data/bin/ruby-parse +16 -0
- data/bin/ruby-rewrite +16 -0
- data/bin/sass +16 -0
- data/bin/sass-convert +16 -0
- data/bin/scss +16 -0
- data/bin/scss-lint +16 -0
- data/bin/term_display +16 -0
- data/bin/term_mandel +16 -0
- data/bin/thor +16 -0
- data/bin/yard +16 -0
- data/bin/yardoc +16 -0
- data/bin/yardstick +16 -0
- data/bin/yri +16 -0
- data/celluloid_pubsub.gemspec +39 -0
- data/init.rb +1 -0
- data/lib/celluloid_pubsub/client_pubsub.rb +71 -0
- data/lib/celluloid_pubsub/reactor.rb +114 -0
- data/lib/celluloid_pubsub/registry.rb +9 -0
- data/lib/celluloid_pubsub/version.rb +14 -0
- data/lib/celluloid_pubsub/web_server.rb +92 -0
- data/lib/celluloid_pubsub.rb +6 -0
- data/spec/lib/celluloid_pubsub/client_pubsub_spec.rb +9 -0
- data/spec/lib/celluloid_pubsub/reactor_spec.rb +6 -0
- data/spec/lib/celluloid_pubsub/registry_spec.rb +6 -0
- data/spec/lib/celluloid_pubsub/web_server_spec.rb +6 -0
- data/spec/spec_helper.rb +48 -0
- metadata +459 -0
data/bin/restclient
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#
|
3
|
+
# This file was generated by Bundler.
|
4
|
+
#
|
5
|
+
# The application 'restclient' is installed as part of a gem, and
|
6
|
+
# this file is here to facilitate running it.
|
7
|
+
#
|
8
|
+
|
9
|
+
require 'pathname'
|
10
|
+
ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
|
11
|
+
Pathname.new(__FILE__).realpath)
|
12
|
+
|
13
|
+
require 'rubygems'
|
14
|
+
require 'bundler/setup'
|
15
|
+
|
16
|
+
load Gem.bin_path('rest-client', 'restclient')
|
data/bin/rspec
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#
|
3
|
+
# This file was generated by Bundler.
|
4
|
+
#
|
5
|
+
# The application 'rspec' is installed as part of a gem, and
|
6
|
+
# this file is here to facilitate running it.
|
7
|
+
#
|
8
|
+
|
9
|
+
require 'pathname'
|
10
|
+
ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
|
11
|
+
Pathname.new(__FILE__).realpath)
|
12
|
+
|
13
|
+
require 'rubygems'
|
14
|
+
require 'bundler/setup'
|
15
|
+
|
16
|
+
load Gem.bin_path('rspec-core', 'rspec')
|
data/bin/rubocop
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#
|
3
|
+
# This file was generated by Bundler.
|
4
|
+
#
|
5
|
+
# The application 'rubocop' is installed as part of a gem, and
|
6
|
+
# this file is here to facilitate running it.
|
7
|
+
#
|
8
|
+
|
9
|
+
require 'pathname'
|
10
|
+
ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
|
11
|
+
Pathname.new(__FILE__).realpath)
|
12
|
+
|
13
|
+
require 'rubygems'
|
14
|
+
require 'bundler/setup'
|
15
|
+
|
16
|
+
load Gem.bin_path('rubocop', 'rubocop')
|
data/bin/ruby-parse
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#
|
3
|
+
# This file was generated by Bundler.
|
4
|
+
#
|
5
|
+
# The application 'ruby-parse' is installed as part of a gem, and
|
6
|
+
# this file is here to facilitate running it.
|
7
|
+
#
|
8
|
+
|
9
|
+
require 'pathname'
|
10
|
+
ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
|
11
|
+
Pathname.new(__FILE__).realpath)
|
12
|
+
|
13
|
+
require 'rubygems'
|
14
|
+
require 'bundler/setup'
|
15
|
+
|
16
|
+
load Gem.bin_path('parser', 'ruby-parse')
|
data/bin/ruby-rewrite
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#
|
3
|
+
# This file was generated by Bundler.
|
4
|
+
#
|
5
|
+
# The application 'ruby-rewrite' is installed as part of a gem, and
|
6
|
+
# this file is here to facilitate running it.
|
7
|
+
#
|
8
|
+
|
9
|
+
require 'pathname'
|
10
|
+
ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
|
11
|
+
Pathname.new(__FILE__).realpath)
|
12
|
+
|
13
|
+
require 'rubygems'
|
14
|
+
require 'bundler/setup'
|
15
|
+
|
16
|
+
load Gem.bin_path('parser', 'ruby-rewrite')
|
data/bin/sass
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#
|
3
|
+
# This file was generated by Bundler.
|
4
|
+
#
|
5
|
+
# The application 'sass' is installed as part of a gem, and
|
6
|
+
# this file is here to facilitate running it.
|
7
|
+
#
|
8
|
+
|
9
|
+
require 'pathname'
|
10
|
+
ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
|
11
|
+
Pathname.new(__FILE__).realpath)
|
12
|
+
|
13
|
+
require 'rubygems'
|
14
|
+
require 'bundler/setup'
|
15
|
+
|
16
|
+
load Gem.bin_path('sass', 'sass')
|
data/bin/sass-convert
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#
|
3
|
+
# This file was generated by Bundler.
|
4
|
+
#
|
5
|
+
# The application 'sass-convert' is installed as part of a gem, and
|
6
|
+
# this file is here to facilitate running it.
|
7
|
+
#
|
8
|
+
|
9
|
+
require 'pathname'
|
10
|
+
ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
|
11
|
+
Pathname.new(__FILE__).realpath)
|
12
|
+
|
13
|
+
require 'rubygems'
|
14
|
+
require 'bundler/setup'
|
15
|
+
|
16
|
+
load Gem.bin_path('sass', 'sass-convert')
|
data/bin/scss
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#
|
3
|
+
# This file was generated by Bundler.
|
4
|
+
#
|
5
|
+
# The application 'scss' is installed as part of a gem, and
|
6
|
+
# this file is here to facilitate running it.
|
7
|
+
#
|
8
|
+
|
9
|
+
require 'pathname'
|
10
|
+
ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
|
11
|
+
Pathname.new(__FILE__).realpath)
|
12
|
+
|
13
|
+
require 'rubygems'
|
14
|
+
require 'bundler/setup'
|
15
|
+
|
16
|
+
load Gem.bin_path('sass', 'scss')
|
data/bin/scss-lint
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#
|
3
|
+
# This file was generated by Bundler.
|
4
|
+
#
|
5
|
+
# The application 'scss-lint' is installed as part of a gem, and
|
6
|
+
# this file is here to facilitate running it.
|
7
|
+
#
|
8
|
+
|
9
|
+
require 'pathname'
|
10
|
+
ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
|
11
|
+
Pathname.new(__FILE__).realpath)
|
12
|
+
|
13
|
+
require 'rubygems'
|
14
|
+
require 'bundler/setup'
|
15
|
+
|
16
|
+
load Gem.bin_path('scss-lint', 'scss-lint')
|
data/bin/term_display
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#
|
3
|
+
# This file was generated by Bundler.
|
4
|
+
#
|
5
|
+
# The application 'term_display' is installed as part of a gem, and
|
6
|
+
# this file is here to facilitate running it.
|
7
|
+
#
|
8
|
+
|
9
|
+
require 'pathname'
|
10
|
+
ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
|
11
|
+
Pathname.new(__FILE__).realpath)
|
12
|
+
|
13
|
+
require 'rubygems'
|
14
|
+
require 'bundler/setup'
|
15
|
+
|
16
|
+
load Gem.bin_path('term-ansicolor', 'term_display')
|
data/bin/term_mandel
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#
|
3
|
+
# This file was generated by Bundler.
|
4
|
+
#
|
5
|
+
# The application 'term_mandel' is installed as part of a gem, and
|
6
|
+
# this file is here to facilitate running it.
|
7
|
+
#
|
8
|
+
|
9
|
+
require 'pathname'
|
10
|
+
ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
|
11
|
+
Pathname.new(__FILE__).realpath)
|
12
|
+
|
13
|
+
require 'rubygems'
|
14
|
+
require 'bundler/setup'
|
15
|
+
|
16
|
+
load Gem.bin_path('term-ansicolor', 'term_mandel')
|
data/bin/thor
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#
|
3
|
+
# This file was generated by Bundler.
|
4
|
+
#
|
5
|
+
# The application 'thor' is installed as part of a gem, and
|
6
|
+
# this file is here to facilitate running it.
|
7
|
+
#
|
8
|
+
|
9
|
+
require 'pathname'
|
10
|
+
ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
|
11
|
+
Pathname.new(__FILE__).realpath)
|
12
|
+
|
13
|
+
require 'rubygems'
|
14
|
+
require 'bundler/setup'
|
15
|
+
|
16
|
+
load Gem.bin_path('thor', 'thor')
|
data/bin/yard
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#
|
3
|
+
# This file was generated by Bundler.
|
4
|
+
#
|
5
|
+
# The application 'yard' is installed as part of a gem, and
|
6
|
+
# this file is here to facilitate running it.
|
7
|
+
#
|
8
|
+
|
9
|
+
require 'pathname'
|
10
|
+
ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
|
11
|
+
Pathname.new(__FILE__).realpath)
|
12
|
+
|
13
|
+
require 'rubygems'
|
14
|
+
require 'bundler/setup'
|
15
|
+
|
16
|
+
load Gem.bin_path('yard', 'yard')
|
data/bin/yardoc
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#
|
3
|
+
# This file was generated by Bundler.
|
4
|
+
#
|
5
|
+
# The application 'yardoc' is installed as part of a gem, and
|
6
|
+
# this file is here to facilitate running it.
|
7
|
+
#
|
8
|
+
|
9
|
+
require 'pathname'
|
10
|
+
ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
|
11
|
+
Pathname.new(__FILE__).realpath)
|
12
|
+
|
13
|
+
require 'rubygems'
|
14
|
+
require 'bundler/setup'
|
15
|
+
|
16
|
+
load Gem.bin_path('yard', 'yardoc')
|
data/bin/yardstick
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#
|
3
|
+
# This file was generated by Bundler.
|
4
|
+
#
|
5
|
+
# The application 'yardstick' is installed as part of a gem, and
|
6
|
+
# this file is here to facilitate running it.
|
7
|
+
#
|
8
|
+
|
9
|
+
require 'pathname'
|
10
|
+
ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
|
11
|
+
Pathname.new(__FILE__).realpath)
|
12
|
+
|
13
|
+
require 'rubygems'
|
14
|
+
require 'bundler/setup'
|
15
|
+
|
16
|
+
load Gem.bin_path('yardstick', 'yardstick')
|
data/bin/yri
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#
|
3
|
+
# This file was generated by Bundler.
|
4
|
+
#
|
5
|
+
# The application 'yri' is installed as part of a gem, and
|
6
|
+
# this file is here to facilitate running it.
|
7
|
+
#
|
8
|
+
|
9
|
+
require 'pathname'
|
10
|
+
ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
|
11
|
+
Pathname.new(__FILE__).realpath)
|
12
|
+
|
13
|
+
require 'rubygems'
|
14
|
+
require 'bundler/setup'
|
15
|
+
|
16
|
+
load Gem.bin_path('yard', 'yri')
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require File.expand_path('../lib/celluloid_pubsub/version', __FILE__)
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = 'celluloid_pubsub'
|
5
|
+
s.version = CelluloidPubsub.gem_version
|
6
|
+
s.platform = Gem::Platform::RUBY
|
7
|
+
s.summary = 'CelluloidPubsub is a simple ruby implementation of publish subscribe design patterns using celluloid actors and websockets, using Celluloid::Reel server'
|
8
|
+
s.email = 'raoul_ice@yahoo.com'
|
9
|
+
s.homepage = 'http://github.com/bogdanRada/celluloid_pubsub/'
|
10
|
+
s.description = 'CelluloidPubsub is a simple ruby implementation of publish subscribe design patterns using celluloid actors and websockets, using Reel server for inter-process communication'
|
11
|
+
s.authors = ['bogdanRada']
|
12
|
+
s.date = Date.today
|
13
|
+
|
14
|
+
s.licenses = ['MIT']
|
15
|
+
s.files = `git ls-files`.split("\n")
|
16
|
+
s.test_files = s.files.grep(/^(spec)/)
|
17
|
+
s.require_paths = ['lib']
|
18
|
+
|
19
|
+
s.add_runtime_dependency 'celluloid', '~> 0.16.0', '>= 0.16.0'
|
20
|
+
s.add_runtime_dependency 'celluloid-io', '~> 0.16.2', '>= 0.16.2'
|
21
|
+
s.add_runtime_dependency 'reel', '~> 0.5.0', '>= 0.5.0'
|
22
|
+
s.add_runtime_dependency 'celluloid-websocket-client', '0.0.1'
|
23
|
+
s.add_runtime_dependency 'activesupport', '~> 4.2.0', '>= 4.2.0'
|
24
|
+
|
25
|
+
s.add_development_dependency 'rspec-rails', '~> 2.0', '>= 2.0'
|
26
|
+
s.add_development_dependency 'guard', '~> 2.6.1', '>= 2.6'
|
27
|
+
s.add_development_dependency 'guard-rspec', '~> 4.2.9', '>= 4.2'
|
28
|
+
s.add_development_dependency 'simplecov', '~> 0.9', '>= 0.9'
|
29
|
+
s.add_development_dependency 'simplecov-summary', '~> 0.0.4', '>= 0.0.4'
|
30
|
+
s.add_development_dependency 'mocha', '~> 1.1', '>= 1.1'
|
31
|
+
s.add_development_dependency 'coveralls', '~> 0.7', '>= 0.7'
|
32
|
+
s.add_development_dependency 'rvm-tester', '~> 1.1', '>= 1.1'
|
33
|
+
|
34
|
+
s.add_development_dependency 'rubocop', '0.29'
|
35
|
+
s.add_development_dependency 'phare', '~> 0.6', '>= 0.6'
|
36
|
+
s.add_development_dependency 'scss-lint', '~> 0.34', '>= 0.34'
|
37
|
+
s.add_development_dependency 'yard', '~> 0.8.7', '>= 0.8.7'
|
38
|
+
s.add_development_dependency 'yardstick', '~> 0.9.9', '>= 0.9.9'
|
39
|
+
end
|
data/init.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'celluloid_pubsub'
|
@@ -0,0 +1,71 @@
|
|
1
|
+
module CelluloidPubsub
|
2
|
+
class Client
|
3
|
+
class PubSubWorker
|
4
|
+
include Celluloid
|
5
|
+
include Celluloid::Logger
|
6
|
+
attr_reader :proc, :topic, :message
|
7
|
+
|
8
|
+
def initialize(options, &connect_blk)
|
9
|
+
options = options.stringify_keys!
|
10
|
+
@actor = options['actor'].present? ? options['actor'] : nil
|
11
|
+
raise "#{self}: Please provide an actor in the options list!!!" if @actor.blank?
|
12
|
+
@connect_blk = connect_blk
|
13
|
+
@client = Celluloid::WebSocket::Client.new("ws://#{CelluloidPubsub::WebServer::HOST}:#{CelluloidPubsub::WebServer::PORT}#{CelluloidPubsub::WebServer::PATH}", Actor.current)
|
14
|
+
end
|
15
|
+
|
16
|
+
def debug_enabled?
|
17
|
+
CelluloidPubsub::WebServer.debug_enabled?
|
18
|
+
end
|
19
|
+
|
20
|
+
def subscribe(channel)
|
21
|
+
subscription_data = { 'client_action' => 'subscribe', 'channel' => channel }
|
22
|
+
debug("#{self.class} tries to subscribe #{subscription_data}") if debug_enabled?
|
23
|
+
async.chat(subscription_data)
|
24
|
+
end
|
25
|
+
|
26
|
+
def publish(channel, data)
|
27
|
+
publishing_data = { 'client_action' => 'publish', 'channel' => channel, 'data' => data }
|
28
|
+
debug(" #{self.class} publishl to #{channel} message: #{publishing_data}") if debug_enabled?
|
29
|
+
async.chat(publishing_data)
|
30
|
+
end
|
31
|
+
|
32
|
+
def on_open
|
33
|
+
debug("#{self.class} websocket connection opened") if debug_enabled?
|
34
|
+
@connect_blk.call Actor.current
|
35
|
+
end
|
36
|
+
|
37
|
+
def on_message(data)
|
38
|
+
debug("#{self.class} received plain #{data}") if debug_enabled?
|
39
|
+
message = JSON.parse(data)
|
40
|
+
debug("#{self.class} received JSON #{message}") if debug_enabled?
|
41
|
+
@actor.async.on_message(message)
|
42
|
+
end
|
43
|
+
|
44
|
+
def on_close(code, reason)
|
45
|
+
@client.terminate
|
46
|
+
terminate
|
47
|
+
debug("#{self.class} dispatching on close #{code} #{reason}") if debug_enabled?
|
48
|
+
@actor.async.on_close(code, reason)
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
def chat(message)
|
54
|
+
final_message = nil
|
55
|
+
if message.is_a?(Hash)
|
56
|
+
debug("#{self.class} sends #{message.to_json}") if debug_enabled?
|
57
|
+
final_message = message.to_json
|
58
|
+
else
|
59
|
+
text_messsage = JSON.dump(action: 'message', message: message)
|
60
|
+
debug("#{self.class} sends JSON #{text_messsage}") if debug_enabled?
|
61
|
+
final_message = text_messsage
|
62
|
+
end
|
63
|
+
@client.text final_message
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def self.connect(options = {}, &connect_blk)
|
68
|
+
CelluloidPubsub::Client::PubSubWorker.new(options, &connect_blk)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,114 @@
|
|
1
|
+
require_relative './registry'
|
2
|
+
module CelluloidPubsub
|
3
|
+
class Reactor
|
4
|
+
include Celluloid
|
5
|
+
include Celluloid::IO
|
6
|
+
include Celluloid::Logger
|
7
|
+
|
8
|
+
attr_accessor :websocket, :server, :mutex
|
9
|
+
|
10
|
+
def work(websocket, server)
|
11
|
+
@server = server
|
12
|
+
@mutex = Mutex.new
|
13
|
+
@websocket = websocket
|
14
|
+
info "Streaming changes for #{websocket.url}" if @server.debug_enabled?
|
15
|
+
async.run
|
16
|
+
end
|
17
|
+
|
18
|
+
def run
|
19
|
+
while message = @websocket.read
|
20
|
+
handle_websocket_message(message)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def parse_json_data(message)
|
25
|
+
debug "Reactor read message #{message}" if @server.debug_enabled?
|
26
|
+
json_data = nil
|
27
|
+
begin
|
28
|
+
json_data = JSON.parse(message)
|
29
|
+
rescue => e
|
30
|
+
debug "Reactor could not parse #{message} because of #{e.inspect}" if @server.debug_enabled?
|
31
|
+
# do nothing
|
32
|
+
end
|
33
|
+
json_data = message if json_data.nil?
|
34
|
+
json_data
|
35
|
+
end
|
36
|
+
|
37
|
+
def handle_websocket_message(message)
|
38
|
+
json_data = parse_json_data(message)
|
39
|
+
handle_parsed_websocket_message(json_data)
|
40
|
+
end
|
41
|
+
|
42
|
+
def handle_parsed_websocket_message(json_data)
|
43
|
+
if json_data.is_a?(Hash)
|
44
|
+
json_data = json_data.stringify_keys
|
45
|
+
debug "Reactor finds actions for #{json_data}" if @server.debug_enabled?
|
46
|
+
delegate_action(json_data) if json_data['client_action'].present?
|
47
|
+
else
|
48
|
+
handle_unknown_action(json_data)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def delegate_action(json_data)
|
53
|
+
case json_data['client_action']
|
54
|
+
when 'unsubscribe_all'
|
55
|
+
unsubscribe_all
|
56
|
+
when 'unsubscribe'
|
57
|
+
async.unsubscribe_client(json_data['channel'])
|
58
|
+
when 'subscribe'
|
59
|
+
async.start_subscriber(json_data['channel'], json_data)
|
60
|
+
when 'publish'
|
61
|
+
@server.publish_event(json_data['channel'], json_data['data'].to_json)
|
62
|
+
else
|
63
|
+
handle_unknown_action(json_data)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def handle_unknown_action(json_data)
|
68
|
+
debug "Trying to dispatch to server #{json_data}" if @server.debug_enabled?
|
69
|
+
@server.async.handle_dispatched_message(Actor.current, json_data)
|
70
|
+
end
|
71
|
+
|
72
|
+
def unsubscribe_client(channel)
|
73
|
+
return unless channel.present?
|
74
|
+
@websocket.close
|
75
|
+
@server.unsubscribe_client(Actor.current, channel)
|
76
|
+
end
|
77
|
+
|
78
|
+
def shutdown
|
79
|
+
terminate
|
80
|
+
end
|
81
|
+
|
82
|
+
def start_subscriber(channel, message)
|
83
|
+
return unless channel.present?
|
84
|
+
@mutex.lock
|
85
|
+
begin
|
86
|
+
add_subscriber_to_channel(channel, message)
|
87
|
+
debug "Subscribed to #{channel} with #{message}" if @server.debug_enabled?
|
88
|
+
@websocket << message.merge('client_action' => 'successful_subscription', 'channel' => channel).to_json
|
89
|
+
rescue => e
|
90
|
+
raise [e, e.respond_to?(:backtrace) ? e.backtrace : '', channel, message].inspect
|
91
|
+
ensure
|
92
|
+
@mutex.unlock
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def add_subscriber_to_channel(channel, message)
|
97
|
+
CelluloidPubsub::Registry.channels << channel unless CelluloidPubsub::Registry.channels.include?(channel)
|
98
|
+
@server.subscribers[channel] ||= []
|
99
|
+
@server.subscribers[channel] << { reactor: Actor.current, message: message }
|
100
|
+
end
|
101
|
+
|
102
|
+
def unsubscribe_all
|
103
|
+
CelluloidPubsub::Registry.channels.map do |channel|
|
104
|
+
@subscribers[channel].each do |hash|
|
105
|
+
hash[:reactor].websocket.close
|
106
|
+
end
|
107
|
+
@server.subscribers[channel] = []
|
108
|
+
end
|
109
|
+
|
110
|
+
info 'clearing connections' if @server.debug_enabled?
|
111
|
+
shutdown
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module CelluloidPubsub # Returns the version of the currently loaded Rails as a <tt>Gem::Version</tt>
|
2
|
+
def self.gem_version
|
3
|
+
Gem::Version.new VERSION::STRING
|
4
|
+
end
|
5
|
+
|
6
|
+
module VERSION
|
7
|
+
MAJOR = 0
|
8
|
+
MINOR = 0
|
9
|
+
TINY = 1
|
10
|
+
PRE = nil
|
11
|
+
|
12
|
+
STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.')
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
require_relative './reactor'
|
2
|
+
module CelluloidPubsub
|
3
|
+
class WebServer < Reel::Server::HTTP
|
4
|
+
include Celluloid::Logger
|
5
|
+
|
6
|
+
HOST = '0.0.0.0'
|
7
|
+
PORT = 1234
|
8
|
+
PATH = '/ws'
|
9
|
+
|
10
|
+
attr_accessor :options, :subscribers, :backlog
|
11
|
+
|
12
|
+
def initialize(options = {})
|
13
|
+
parse_options(options)
|
14
|
+
@subscribers = {}
|
15
|
+
info "CelluloidPubsub::WebServer example starting on #{@hostname}:#{@port}" if debug_enabled?
|
16
|
+
super(@hostname, @port, { spy: @spy, backlog: @backlog }, &method(:on_connection))
|
17
|
+
end
|
18
|
+
|
19
|
+
def parse_options(options)
|
20
|
+
raise 'Options is not a hash or is not present ' unless options.is_a?(Hash)
|
21
|
+
@options = options.stringify_keys
|
22
|
+
@backlog = @options.fetch(:backlog, 1024)
|
23
|
+
@hostname = @options.fetch(:hostname, CelluloidPubsub::WebServer::HOST)
|
24
|
+
@port = @options.fetch(:port, CelluloidPubsub::WebServer::PORT)
|
25
|
+
@path = @options.fetch(:path, CelluloidPubsub::WebServer::PATH)
|
26
|
+
@spy = @options.fetch(:spy, false)
|
27
|
+
end
|
28
|
+
|
29
|
+
def debug_enabled?
|
30
|
+
self.class.debug_enabled?
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.debug_enabled?
|
34
|
+
ENV['DEBUG_CELLULOID'].present? && (ENV['DEBUG_CELLULOID'] == 'true' || ENV['DEBUG_CELLULOID'] == true)
|
35
|
+
end
|
36
|
+
|
37
|
+
def publish_event(current_topic, message)
|
38
|
+
return if current_topic.blank? || message.blank?
|
39
|
+
@subscribers[current_topic].each do |hash|
|
40
|
+
hash[:reactor].websocket << message
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def on_connection(connection)
|
45
|
+
while request = connection.request
|
46
|
+
if request.websocket?
|
47
|
+
info 'Received a WebSocket connection' if debug_enabled?
|
48
|
+
|
49
|
+
# We're going to hand off this connection to another actor (Writer/Reader)
|
50
|
+
# However, initially Reel::Connections are "attached" to the
|
51
|
+
# Reel::Server::HTTP actor, meaning that the server manages the connection
|
52
|
+
# lifecycle (e.g. error handling) for us.
|
53
|
+
#
|
54
|
+
# If we want to hand this connection off to another actor, we first
|
55
|
+
# need to detach it from the Reel::Server (in this case, Reel::Server::HTTP)
|
56
|
+
connection.detach
|
57
|
+
route_websocket(request.websocket)
|
58
|
+
return
|
59
|
+
else
|
60
|
+
route_request connection, request
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def route_request(connection, request)
|
66
|
+
info "404 Not Found: #{request.path}" if debug_enabled?
|
67
|
+
connection.respond :not_found, 'Not found'
|
68
|
+
end
|
69
|
+
|
70
|
+
def route_websocket(socket)
|
71
|
+
if socket.url == @path
|
72
|
+
info 'Reactor handles new socket connection' if debug_enabled?
|
73
|
+
reactor = CelluloidPubsub::Reactor.new
|
74
|
+
Actor.current.link reactor
|
75
|
+
reactor.async.work(socket, Actor.current)
|
76
|
+
else
|
77
|
+
info "Received invalid WebSocket request for: #{socket.url}" if debug_enabled?
|
78
|
+
socket.close
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def handle_dispatched_message(reactor, data)
|
83
|
+
debug "Webserver trying to dispatch message #{data.inspect}" if debug_enabled?
|
84
|
+
message = reactor.parse_json_data(data)
|
85
|
+
if message.present? && message.is_a?(Hash)
|
86
|
+
reactor.websocket << message.to_json
|
87
|
+
else
|
88
|
+
reactor.websocket << data.to_json
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|