resque-pubsub 0.1.2 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,4 +1,4 @@
1
- Copyright (c) 2011 [name of plugin creator]
1
+ Copyright (c) 2011 Monica McArthur
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
data/README.md CHANGED
@@ -11,26 +11,26 @@ Usage / Examples
11
11
 
12
12
  A simple class that can publish a message:
13
13
 
14
- class TestPublisher
15
- require 'resque-pubsub'
14
+ class TestPublisher
15
+ include Resque::Plugins::Pubsub::Publisher
16
16
 
17
- def some_method
18
- self.publish(topic, message)
17
+ def some_method
18
+ self.publish(topic, message)
19
+ end
19
20
  end
20
- end
21
21
 
22
22
 
23
23
  A simple class that subscribes to messages on a particular topic:
24
24
 
25
- class TestSubscriber
26
- require 'resque-pubsub'
25
+ class TestSubscriber
26
+ include Resque::Plugins::Pubsub::Subscriber
27
27
 
28
- subscribe 'test_topic'
28
+ subscribe 'test_topic'
29
29
 
30
- def self.read_test_topic_message(message)
31
- # Do something with the message
30
+ def self.read_test_topic_message(message)
31
+ # Do something with the message
32
+ end
32
33
  end
33
- end
34
34
 
35
35
 
36
36
  Customize & Extend
@@ -63,13 +63,13 @@ Running Resque
63
63
  A sample config file is provided in examples/resque-pubsub.rb. If you put this in config/initializers for a Rails app,
64
64
  then Resque will default to the app namespace but will take an override on namespace from the environment variable RESQUE_NAMESPACE. Thus
65
65
 
66
- QUEUE=* RESQUE_NAMESPACE="resque:pubsub" rake environment resque:work
66
+ QUEUE=* RESQUE_NAMESPACE="resque:pubsub" rake environment resque:work
67
67
 
68
68
  will run resque jobs against the default pubsub namespace (i.e., will be the pubsub server)
69
69
 
70
70
  while
71
71
 
72
- QUEUE=* rake environment resque:work
72
+ QUEUE=* rake environment resque:work
73
73
 
74
74
  will run resque in an app as normal.
75
75
 
@@ -4,7 +4,7 @@ rails_root = ENV['RAILS_ROOT'] || File.dirname(__FILE__) + '/../../../..'
4
4
  rails_env = ENV['RAILS_ENV'] || 'development'
5
5
 
6
6
  REDIS_CONFIG = YAML.load_file(rails_root + '/config/redis.yml')[rails_env]
7
- Resque.redis = REDIS_CONFIG["host"] + ':' + REDIS_CONFIG["port"].to_s
7
+ Resque.redis = REDIS_CONFIG['host'] + ':' + REDIS_CONFIG["port"].to_s
8
8
 
9
9
  namespace = ENV['RESQUE_NAMESPACE'] || 'mynamespace' # Edit this to use a different namespace for Resque in the app
10
10
  Resque.redis.namespace = namespace
@@ -1,6 +1,2 @@
1
- require 'resque/plugins/pubsub/subscriber'
2
- require 'resque/plugins/pubsub/publisher'
3
- require 'resque/plugins/pubsub/broker'
4
- self.send(:include, Resque::Plugins::Pubsub::Subscriber)
5
- self.send(:include, Resque::Plugins::Pubsub::Publisher)
6
-
1
+ require 'resque'
2
+ Dir.glob(File.expand_path('resque/plugins/pubsub/*', File.dirname(__FILE__))).each { |filename| require filename }
@@ -1,31 +1,34 @@
1
1
  module Resque
2
2
  module Plugins
3
- #
4
- # pubsub broker
5
- #
6
3
  module Pubsub
7
4
  class Broker
5
+
8
6
  @queue = :messages
9
7
  # Returns a top-level Redis connection so that the broker can distribute messages
10
8
  # across namespaces. If none has been created, will
11
9
  # create a new one using information from the Resque connection.
12
- def self.redis
13
- return @redis if @redis
14
- client_to_copy = Resque.redis.client
15
- @redis = Redis.new(:host => client_to_copy.host, :port => client_to_copy.port,
16
- :thread_safe => true, :db => client_to_copy.db)
17
- end
18
-
19
- def self.perform(topic, message)
20
- subscribers = Exchange.redis.smembers("#{topic}_subscribers")
21
- subscribers.each {|s|
22
- sinfo = JSON.parse(s)
23
- puts "distributing to #{sinfo.inspect}"
24
- Broker.redis.sadd("#{sinfo['namespace']}:queues", "fanout:#{topic}")
25
- Broker.redis.rpush("#{sinfo['namespace']}:queue:fanout:#{topic}", {:class=> sinfo["class"], :args=>[message]}.to_json)
26
- }
10
+
11
+ class << self
12
+
13
+ def redis
14
+ return @redis if @redis
15
+ client_to_copy = Resque.redis.client
16
+ @redis = Redis.new(:host => client_to_copy.host, :port => client_to_copy.port, :thread_safe => true, :db => client_to_copy.db)
17
+ end
18
+
19
+ def perform(topic, message)
20
+ subscribers = Exchange.redis.smembers("#{topic}_subscribers")
21
+ subscribers.each do |s|
22
+ sinfo = JSON.parse(s)
23
+ puts "[Broker] distributing to #{sinfo.inspect}"
24
+ Broker.redis.sadd("#{sinfo['namespace']}:queues", "fanout:#{topic}")
25
+ Broker.redis.rpush("#{sinfo['namespace']}:queue:fanout:#{topic}", { :class => sinfo['class'], :args => [message] }.to_json)
26
+ end
27
+ end
28
+
27
29
  end
30
+
28
31
  end
29
32
  end
30
33
  end
31
- end
34
+ end
@@ -1,42 +1,39 @@
1
- require 'resque'
2
1
  module Resque
3
2
  module Plugins
4
- #
5
- # pubsub exchange manager
6
- #
7
3
  module Pubsub
8
4
  class Exchange
9
- @@pubsub_namespace = nil
10
- # Returns the current Redis connection. If none has been created, will
11
- # create a new one using information from the Resque connection and our config.
12
- def self.redis
13
- return @redis if @redis
14
- client_to_copy = Resque.redis.client
15
- redis_new = Redis.new(:host => client_to_copy.host, :port => client_to_copy.port,
16
- :thread_safe => true, :db => client_to_copy.db)
17
- puts "making a redis in exchange, namespace will be #{@@pubsub_namespace}"
18
- @redis = Redis::Namespace.new(@@pubsub_namespace || "resque:pubsub", :redis => redis_new)
19
- end
20
5
 
21
6
  @queue = :subscription_requests
22
-
23
- def self.perform(subscription_info)
24
- puts "handling a subscription on the exchange"
25
- puts "requested subscription is #{subscription_info.inspect}"
26
- puts "namespace is #{Exchange.redis.namespace}"
27
- redis = Exchange.redis
28
- redis.sadd("#{subscription_info["topic"]}_subscribers", {:class=>subscription_info["class"], :namespace=>subscription_info["namespace"]}.to_json)
29
- end
30
-
31
- def self.pubsub_namespace
32
- @@pubsub_namespace
33
- end
34
-
35
- def self.pubsub_namespace=(n)
36
- @@pubsub_namespace = n
37
- @redis.client.disconnect
38
- @redis = nil
7
+
8
+ class << self
9
+
10
+ def redis
11
+ return @redis if @redis
12
+ client_to_copy = Resque.redis.client
13
+ redis_new = Redis.new(:host => client_to_copy.host, :port => client_to_copy.port, :thread_safe => true, :db => client_to_copy.db)
14
+ puts "[Exchange] making a redis in exchange, namespace will be #{@pubsub_namespace}"
15
+ @redis = Redis::Namespace.new(@pubsub_namespace || 'resque:pubsub', :redis => redis_new)
16
+ end
17
+
18
+ def perform(subscription_info)
19
+ puts '[Exchange] handling a subscription on the exchange'
20
+ puts "[Exchange] requested subscription is #{subscription_info.inspect}"
21
+ puts "[Exchange] namespace is #{Exchange.redis.namespace}"
22
+ Exchange.redis.sadd("#{subscription_info["topic"]}_subscribers", { :class => subscription_info['class'], :namespace => subscription_info['namespace'] }.to_json)
23
+ end
24
+
25
+ def pubsub_namespace
26
+ @pubsub_namespace
27
+ end
28
+
29
+ def pubsub_namespace=(namespace)
30
+ @pubsub_namespace = namespace
31
+ @redis.client.disconnect if @redis
32
+ @redis = nil
33
+ end
34
+
39
35
  end
36
+
40
37
  end
41
38
  end
42
39
  end
@@ -1,23 +1,22 @@
1
- require 'resque/plugins/pubsub/exchange'
2
1
  module Resque
3
2
  module Plugins
4
- #
5
- # pubsub publisher
6
- #
7
3
  module Pubsub
8
4
  module Publisher
9
- def self.included(base)
10
- puts 'including publisher'
5
+
6
+ def self.included(base)
11
7
  base.send(:include, InstanceMethods)
12
8
  end
13
9
 
14
10
  module InstanceMethods
11
+
15
12
  def publish(topic, message)
16
- puts "Publisher publishing #{message} in #{topic}"
17
- Exchange.redis.sadd(:queues, "messages")
18
- Exchange.redis.rpush("queue:messages", {:class=>'Resque::Plugins::Pubsub::Broker', :args=>[topic, message]}.to_json)
13
+ puts "[#{self.class.to_s}] publishing #{message} in #{topic}"
14
+ Exchange.redis.sadd(:queues, 'messages')
15
+ Exchange.redis.rpush('queue:messages', { :class => 'Resque::Plugins::Pubsub::Broker', :args => [topic, message] }.to_json)
19
16
  end
17
+
20
18
  end
19
+
21
20
  end
22
21
  end
23
22
  end
@@ -1,33 +1,32 @@
1
- require 'resque/plugins/pubsub/exchange'
2
1
  module Resque
3
2
  module Plugins
4
- #
5
- # pubsub subscriber
6
- #
7
3
  module Pubsub
8
4
  module Subscriber
9
- def self.included(base)
10
- base.extend ClassMethods
5
+
6
+ def self.included(base)
7
+ base.send(:extend, ClassMethods)
11
8
  end
12
-
9
+
13
10
  module ClassMethods
11
+
14
12
  def subscribe(topic, options={})
15
13
  @queue = "fanout:#{topic}"
16
14
  reader_method = options[:reader_method] || "read_#{topic}_message"
17
15
  module_eval <<-"end_eval"
18
16
  def self.perform(message)
19
- self.send("#{reader_method}", message)
20
- end
17
+ self.send("#{reader_method.to_s}", message)
18
+ end
21
19
  end_eval
22
20
  options[:namespace] = Resque.redis.namespace
23
21
  options[:topic] = topic
24
22
  options[:class] = self.to_s
25
- puts "Subscriber subscribing with #{options.inspect}"
23
+ puts "[#{self.to_s}] subscribing with #{options.inspect}"
26
24
  Exchange.redis.sadd(:queues, :subscription_requests)
27
- Exchange.redis.rpush("queue:subscription_requests", {:class=>'Resque::Plugins::Pubsub::Exchange', :args=>[options]}.to_json)
25
+ Exchange.redis.rpush("queue:subscription_requests", { :class => 'Resque::Plugins::Pubsub::Exchange', :args => [options] }.to_json)
28
26
  end
29
27
 
30
28
  end
29
+
31
30
  end
32
31
  end
33
32
  end
@@ -0,0 +1,19 @@
1
+ class TestCustomSubscriber
2
+ include Resque::Plugins::Pubsub::Subscriber
3
+
4
+ subscribe 'test_custom_topic', :reader_method => :simple
5
+
6
+ class << self
7
+
8
+ def simple(message)
9
+ puts "[#{self.to_s}] got test custom topic message: #{message.inspect}"
10
+ @last_message = message
11
+ end
12
+
13
+ def last_message
14
+ @last_message
15
+ end
16
+
17
+ end
18
+
19
+ end
@@ -0,0 +1,3 @@
1
+ class TestPublisher
2
+ include Resque::Plugins::Pubsub::Publisher
3
+ end
@@ -0,0 +1,19 @@
1
+ class TestSubscriber
2
+ include Resque::Plugins::Pubsub::Subscriber
3
+
4
+ subscribe 'test_topic'
5
+
6
+ class << self
7
+
8
+ def read_test_topic_message(message)
9
+ puts "[#{self.to_s}] got test topic message: #{message.inspect}"
10
+ @last_message = message
11
+ end
12
+
13
+ def last_message
14
+ @last_message
15
+ end
16
+
17
+ end
18
+
19
+ end
@@ -1,8 +1,10 @@
1
- require File.dirname(__FILE__) + '/test_helper'
1
+ require File.expand_path('test_helper.rb', File.dirname(__FILE__))
2
2
 
3
3
  class PubsubTest < Test::Unit::TestCase
4
+
4
5
  def setup
5
6
  $success = $lock_failed = $lock_expired = 0
7
+ Resque.redis.namespace = nil
6
8
  Resque.redis.flushall
7
9
  Resque.redis.namespace = 'test_pubsub'
8
10
  end
@@ -12,72 +14,63 @@ class PubsubTest < Test::Unit::TestCase
12
14
  Resque::Plugin.lint(Resque::Plugins::Pubsub)
13
15
  end
14
16
  end
15
-
17
+
16
18
  def test_all
17
- # Just loading the "TestSubscriber" class is enough to subscribe
18
- require 'test_publisher'
19
- require 'test_subscriber'
19
+ TestSubscriber.subscribe('test_topic')
20
20
  # Now process the subscription
21
21
  Resque.redis.namespace = 'resque:pubsub'
22
- @worker = Resque::Worker.new(:subscription_requests)
23
- @worker.process
22
+ Resque::Worker.new(:subscription_requests).process
24
23
  # Check that the subscription is in the subscribers list
25
- assert check_subscription(Resque.redis.smembers("test_topic_subscribers"), "TestSubscriber", "test_pubsub")
24
+ assert subscription_exists(Resque.redis.smembers('test_topic_subscribers'), 'TestSubscriber', 'test_pubsub')
25
+
26
26
  p = TestPublisher.new
27
- p.publish("test_topic", "Test message")
27
+ p.publish('test_topic', 'Test message')
28
28
  # Run Resque for the broker
29
29
  Resque.redis.namespace = 'resque:pubsub'
30
- @worker = Resque::Worker.new(:messages)
31
- @worker.process
30
+ Resque::Worker.new(:messages).process
32
31
  # Check that the message queue has been populated
33
32
  Resque.redis.namespace = 'test_pubsub'
34
33
  assert Resque.redis.keys.include?('queue:fanout:test_topic')
35
- assert Resque.redis.llen('queue:fanout:test_topic')==1
34
+ assert_equal 1, Resque.redis.llen('queue:fanout:test_topic')
35
+
36
36
  # Now run the subscriber Resque
37
- @worker = Resque::Worker.new('fanout:test_topic')
38
- @worker.process
39
- assert Resque.redis.llen('fanout:test_topic')==0
40
- assert TestSubscriber.last_message=='Test message'
37
+ Resque::Worker.new('fanout:test_topic').process
38
+ assert_equal 0, Resque.redis.llen('queue:fanout:test_topic')
39
+ assert_equal 'Test message', TestSubscriber.last_message
41
40
  end
42
-
41
+
43
42
  def test_configuration_options
44
- # Configure the pubsub namespace
45
- require 'resque-pubsub'
46
43
  Resque::Plugins::Pubsub::Exchange.pubsub_namespace = 'resque:custom_space'
47
- puts "namespace is set to #{Resque::Plugins::Pubsub::Exchange.pubsub_namespace}"
48
- require 'test_publisher'
49
- require 'test_subscriber'
50
- require 'test_subscriber_custom'
44
+ TestCustomSubscriber.subscribe('test_custom_topic', :reader_method => :simple)
51
45
  # Now process the subscription
52
46
  Resque.redis.namespace = 'resque:custom_space'
53
- @worker = Resque::Worker.new(:subscription_requests)
54
- @worker.process
47
+ Resque::Worker.new(:subscription_requests).process
55
48
  # Check that the subscription is in the subscribers list
56
- assert check_subscription(Resque.redis.smembers("test_custom_topic_subscribers"), "TestSubscriberCustom", "test_pubsub")
57
- p = TestPublisher.new
58
- p.publish("test_custom_topic", "Test custom message")
49
+ assert subscription_exists(Resque.redis.smembers('test_custom_topic_subscribers'), 'TestCustomSubscriber', 'test_pubsub')
50
+
51
+ TestPublisher.new.publish('test_custom_topic', 'Test custom message')
59
52
  # Run Resque for the broker
60
53
  Resque.redis.namespace = 'resque:custom_space'
61
- @worker = Resque::Worker.new(:messages)
62
- @worker.process
54
+ Resque::Worker.new(:messages).process
63
55
  # Check that the message queue has been populated
64
56
  Resque.redis.namespace = 'test_pubsub'
65
57
  assert Resque.redis.keys.include?('queue:fanout:test_custom_topic')
66
- assert Resque.redis.llen('queue:fanout:test_custom_topic')==1
58
+ assert_equal 1, Resque.redis.llen('queue:fanout:test_custom_topic')
59
+
67
60
  # Now run the subscriber Resque
68
- @worker = Resque::Worker.new('fanout:test_custom_topic')
69
- @worker.process
70
- assert Resque.redis.llen('fanout:test_custom_topic')==0
71
- assert TestSubscriberCustom.last_message=='Test custom message'
72
- # Also make sure TestSubscriber DIDN'T get the message
73
- assert TestSubscriber.last_message!='Test custom message'
61
+ Resque::Worker.new('fanout:test_custom_topic').process
62
+ assert_equal 0, Resque.redis.llen('queue:fanout:test_custom_topic')
63
+ assert_equal 'Test custom message', TestCustomSubscriber.last_message
64
+ assert_not_equal 'Test custom message', TestSubscriber.last_message
74
65
  end
66
+
67
+ private
75
68
 
76
- def check_subscription(subscribers, klass, namespace)
77
- subscribers.inject(false) {|result, s|
69
+ def subscription_exists(subscribers, klass, namespace)
70
+ subscribers.inject(false) do |result, s|
78
71
  sinfo = JSON.parse(s)
79
- result = result || (sinfo["class"]==klass && sinfo['namespace']==namespace)
80
- }
72
+ result = result || (sinfo['class'] == klass && sinfo['namespace'] == namespace)
73
+ end
81
74
  end
82
75
 
83
- end
76
+ end
@@ -10,15 +10,13 @@ require 'active_support/test_case'
10
10
 
11
11
  require 'resque-pubsub'
12
12
 
13
- ##
14
13
  # make sure we can run redis
15
- if !system("which redis-server")
14
+ if !system('which redis-server')
16
15
  puts '', "** can't find `redis-server` in your path"
17
16
  puts "** try running `sudo rake install`"
18
17
  abort ''
19
18
  end
20
19
 
21
- ##
22
20
  # start our own redis when the tests start,
23
21
  # kill it when they end
24
22
  at_exit do
@@ -31,12 +29,15 @@ at_exit do
31
29
  end
32
30
 
33
31
  pid = `ps -e -o pid,command | grep [r]edis-test`.split(" ")[0]
34
- puts "Killing test redis server..."
32
+ puts 'Killing test redis server...'
35
33
  `rm -f #{dir}/dump.rdb`
36
- Process.kill("KILL", pid.to_i)
34
+ Process.kill('KILL', pid.to_i)
37
35
  exit exit_code
38
36
  end
39
37
 
40
- puts "Starting redis for testing at localhost:9736..."
38
+ puts 'Starting redis for testing at localhost:9736...'
41
39
  `redis-server #{dir}/redis-test.conf`
42
- Resque.redis = '127.0.0.1:9736'
40
+ Resque.redis = '127.0.0.1:9736'
41
+ Resque.redis.namespace = 'test_pubsub'
42
+
43
+ Dir.glob(File.expand_path(dir + '/fixtures/*')).each { |filename| require filename }
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: resque-pubsub
3
3
  version: !ruby/object:Gem::Version
4
- hash: 31
4
+ hash: 23
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
- - 1
9
8
  - 2
10
- version: 0.1.2
9
+ - 0
10
+ version: 0.2.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - Monica McArthur
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2011-04-08 00:00:00 -07:00
18
+ date: 2011-04-12 00:00:00 -07:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
@@ -34,7 +34,7 @@ dependencies:
34
34
  version: 1.9.10
35
35
  type: :runtime
36
36
  version_requirements: *id001
37
- description: " A Resque plugin. Provides a lightweight publish/subscribe messaging system, with message persistence when clients are down.\n"
37
+ description: " A Resque plugin. Provides a lightweight publish/subscribe messaging system, with message persistence when clients are down.\n"
38
38
  email: mechaferret@gmail.com
39
39
  executables: []
40
40
 
@@ -53,13 +53,12 @@ files:
53
53
  - lib/resque/plugins/pubsub/publisher.rb
54
54
  - lib/resque/plugins/pubsub/subscriber.rb
55
55
  - lib/resque-pubsub.rb
56
- - test/debug.log
56
+ - test/fixtures/test_custom_subscriber.rb
57
+ - test/fixtures/test_publisher.rb
58
+ - test/fixtures/test_subscriber.rb
57
59
  - test/pubsub_test.rb
58
60
  - test/redis-test.conf
59
61
  - test/test_helper.rb
60
- - test/test_publisher.rb
61
- - test/test_subscriber.rb
62
- - test/test_subscriber_custom.rb
63
62
  has_rdoc: true
64
63
  homepage: http://github.com/mechaferret/resque-pubsub
65
64
  licenses: []
@@ -1,16 +0,0 @@
1
- # Logfile created on Thu Jan 20 18:20:02 -0800 2011 by logger.rb/22285
2
- SQL (0.2ms) SET SQL_AUTO_IS_NULL=0
3
- SQL (0.3ms) SHOW TABLES
4
- SQL (430.6ms) CREATE TABLE `transactionals` (`id` int(11) DEFAULT NULL auto_increment PRIMARY KEY, `name` varchar(255), `description` varchar(255), `other_id` int(11)) ENGINE=InnoDB
5
- SQL (0.4ms) SHOW TABLES
6
- SQL (114.4ms) CREATE TABLE `transactional_facts` (`id` int(11) DEFAULT NULL auto_increment PRIMARY KEY, `name` varchar(255), `description` varchar(255), `other_id` int(11)) ENGINE=InnoDB
7
- SQL (0.4ms) SHOW TABLES
8
- SQL (102.1ms) CREATE TABLE `schema_migrations` (`version` varchar(255) NOT NULL) ENGINE=InnoDB
9
- SQL (172.5ms) CREATE UNIQUE INDEX `unique_schema_migrations` ON `schema_migrations` (`version`)
10
- SQL (0.4ms) SHOW TABLES
11
- SQL (0.1ms) SELECT version FROM `schema_migrations`
12
- SQL (0.4ms) INSERT INTO `schema_migrations` (version) VALUES ('3')
13
- Transactional Columns (38.0ms) SHOW FIELDS FROM `transactionals`
14
- SQL (0.3ms) BEGIN
15
- Transactional Create (0.3ms) INSERT INTO `transactionals` (`name`, `other_id`, `description`) VALUES(NULL, NULL, NULL)
16
- SQL (0.7ms) COMMIT
@@ -1,4 +0,0 @@
1
- class TestPublisher
2
- require 'resque-pubsub'
3
-
4
- end
@@ -1,17 +0,0 @@
1
- class TestSubscriber
2
- require 'resque-pubsub'
3
-
4
- subscribe 'test_topic'
5
-
6
- @@last_message = nil
7
-
8
- def self.read_test_topic_message(message)
9
- puts "got test topic message: #{message.inspect}"
10
- @@last_message = message
11
- end
12
-
13
- def self.last_message
14
- @@last_message
15
- end
16
-
17
- end
@@ -1,17 +0,0 @@
1
- class TestSubscriberCustom
2
- require 'resque-pubsub'
3
-
4
- subscribe 'test_custom_topic', :reader_method => 'simple'
5
-
6
- @@last_message = nil
7
-
8
- def self.simple(message)
9
- puts "in simple, got test custom topic message: #{message.inspect}"
10
- @@last_message = message
11
- end
12
-
13
- def self.last_message
14
- @@last_message
15
- end
16
-
17
- end