celluloid_pubsub 0.0.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 +7 -0
  2. data/.coveralls.yml +2 -0
  3. data/.gitignore +16 -0
  4. data/.rspec +1 -0
  5. data/.rubocop.yml +68 -0
  6. data/.travis.yml +12 -0
  7. data/CONTRIBUTING.md +44 -0
  8. data/Gemfile +3 -0
  9. data/Guardfile +12 -0
  10. data/LICENSE +20 -0
  11. data/README.rdoc +96 -0
  12. data/Rakefile +42 -0
  13. data/bin/appraisal +16 -0
  14. data/bin/autospec +16 -0
  15. data/bin/bundler +16 -0
  16. data/bin/cdiff +16 -0
  17. data/bin/coderay +16 -0
  18. data/bin/colortab +16 -0
  19. data/bin/coveralls +16 -0
  20. data/bin/decolor +16 -0
  21. data/bin/erubis +16 -0
  22. data/bin/guard +16 -0
  23. data/bin/htmldiff +16 -0
  24. data/bin/ldiff +16 -0
  25. data/bin/listen +16 -0
  26. data/bin/nokogiri +16 -0
  27. data/bin/phare +16 -0
  28. data/bin/pry +16 -0
  29. data/bin/rackup +16 -0
  30. data/bin/rails +16 -0
  31. data/bin/rake +16 -0
  32. data/bin/restclient +16 -0
  33. data/bin/rspec +16 -0
  34. data/bin/rubocop +16 -0
  35. data/bin/ruby-parse +16 -0
  36. data/bin/ruby-rewrite +16 -0
  37. data/bin/sass +16 -0
  38. data/bin/sass-convert +16 -0
  39. data/bin/scss +16 -0
  40. data/bin/scss-lint +16 -0
  41. data/bin/term_display +16 -0
  42. data/bin/term_mandel +16 -0
  43. data/bin/thor +16 -0
  44. data/bin/yard +16 -0
  45. data/bin/yardoc +16 -0
  46. data/bin/yardstick +16 -0
  47. data/bin/yri +16 -0
  48. data/celluloid_pubsub.gemspec +39 -0
  49. data/init.rb +1 -0
  50. data/lib/celluloid_pubsub/client_pubsub.rb +71 -0
  51. data/lib/celluloid_pubsub/reactor.rb +114 -0
  52. data/lib/celluloid_pubsub/registry.rb +9 -0
  53. data/lib/celluloid_pubsub/version.rb +14 -0
  54. data/lib/celluloid_pubsub/web_server.rb +92 -0
  55. data/lib/celluloid_pubsub.rb +6 -0
  56. data/spec/lib/celluloid_pubsub/client_pubsub_spec.rb +9 -0
  57. data/spec/lib/celluloid_pubsub/reactor_spec.rb +6 -0
  58. data/spec/lib/celluloid_pubsub/registry_spec.rb +6 -0
  59. data/spec/lib/celluloid_pubsub/web_server_spec.rb +6 -0
  60. data/spec/spec_helper.rb +48 -0
  61. 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,9 @@
1
+ module CelluloidPubsub
2
+ class Registry
3
+ include ActiveSupport::Configurable
4
+ class << self
5
+ attr_accessor :channels
6
+ end
7
+ @channels = []
8
+ end
9
+ 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
@@ -0,0 +1,6 @@
1
+ require 'celluloid'
2
+ require 'celluloid/io'
3
+ require 'reel'
4
+ require 'celluloid/websocket/client'
5
+ require 'active_support/all'
6
+ Gem.find_files('celluloid_pubsub/**/*.rb').each { |path| require path }
@@ -0,0 +1,9 @@
1
+ # encoding:utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ describe CelluloidPubsub::Client do
6
+ end
7
+
8
+ describe CelluloidPubsub::Client::PubSubWorker do
9
+ end
@@ -0,0 +1,6 @@
1
+ # encoding:utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ describe CelluloidPubsub::Reactor do
6
+ end
@@ -0,0 +1,6 @@
1
+ # encoding:utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ describe CelluloidPubsub::Registry do
6
+ end
@@ -0,0 +1,6 @@
1
+ # encoding:utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ describe CelluloidPubsub::WebServer do
6
+ end