honest_pubsub 0.2.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. checksums.yaml +7 -0
  2. data/#.ruby-gemset# +0 -0
  3. data/.gitignore +22 -0
  4. data/.ruby-version +1 -0
  5. data/Gemfile +4 -0
  6. data/LICENSE.txt +22 -0
  7. data/README.md +92 -0
  8. data/Rakefile +1 -0
  9. data/bin/start_subscribers +14 -0
  10. data/config/pubsub.yml +17 -0
  11. data/honest_pubsub.gemspec +38 -0
  12. data/lib/honest_pubsub/cli.rb +94 -0
  13. data/lib/honest_pubsub/configuration.rb +38 -0
  14. data/lib/honest_pubsub/context.rb +70 -0
  15. data/lib/honest_pubsub/db_logger.rb +52 -0
  16. data/lib/honest_pubsub/exceptions/payload_error.rb +2 -0
  17. data/lib/honest_pubsub/logger.rb +49 -0
  18. data/lib/honest_pubsub/logging.rb +42 -0
  19. data/lib/honest_pubsub/message.rb +50 -0
  20. data/lib/honest_pubsub/middleware.rb +14 -0
  21. data/lib/honest_pubsub/publisher.rb +93 -0
  22. data/lib/honest_pubsub/railtie.rb +27 -0
  23. data/lib/honest_pubsub/server/client_queue_listener.rb +54 -0
  24. data/lib/honest_pubsub/server/client_worker.rb +86 -0
  25. data/lib/honest_pubsub/server/subscriber_server.rb +84 -0
  26. data/lib/honest_pubsub/server.rb +8 -0
  27. data/lib/honest_pubsub/subscriber.rb +69 -0
  28. data/lib/honest_pubsub/version.rb +3 -0
  29. data/lib/honest_pubsub.rb +46 -0
  30. data/spec/config.yml +20 -0
  31. data/spec/honest_pubsub/#subscriber_spec.rb# +9 -0
  32. data/spec/honest_pubsub/cli_spec.rb +145 -0
  33. data/spec/honest_pubsub/server/client_queue_listener_spec.rb +76 -0
  34. data/spec/honest_pubsub/server/client_worker_spec.rb +161 -0
  35. data/spec/honest_pubsub/subscriber_spec.rb +5 -0
  36. data/spec/logger_spec.rb +110 -0
  37. data/spec/message_spec.rb +65 -0
  38. data/spec/spec_helper.rb +49 -0
  39. metadata +259 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: cc6a19fcd7590a3594ce7dce30c525ffad8d862e
4
+ data.tar.gz: 3904cd4bde4b1498a993d868a0fc7c8b8bd0cec6
5
+ SHA512:
6
+ metadata.gz: 07e917724a4763ceea12384d38ecd153fbc22d9055b648eb6ead02832512491394b7d1ecb4690b66c670cf0afd17a907dba57ccb9fcdc6981ef662621c9565ad
7
+ data.tar.gz: b28f6207461e9c0f0b09d31fda4c2819fb92cdab523f0e96294198a27e2d41895ea63d0f90310c5d4fa1907d54f6b15b194ddca0a51c5ae217cd64955d0b07ea
data/#.ruby-gemset# ADDED
File without changes
data/.gitignore ADDED
@@ -0,0 +1,22 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ .project
7
+ .ruby-gemset
8
+ Gemfile.lock
9
+ InstalledFiles
10
+ _yardoc
11
+ coverage
12
+ doc/
13
+ lib/bundler/man
14
+ pkg
15
+ rdoc
16
+ spec/reports
17
+ test/tmp
18
+ test/version_tmp
19
+ tmp
20
+ .rspec
21
+ .idea
22
+ test_pubsub.log
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 2.0
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in honest_pubsub.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Thanh Lim
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,92 @@
1
+ # HonestPubsub
2
+
3
+ Simple Publishers and subscribers for Ruby using RabbitMQ
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'honest_pubsub'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install honest_pubsub
18
+
19
+ You also need to install RabbitMQ
20
+
21
+ $ brew install rabbitmq
22
+
23
+ ## Usage
24
+
25
+ There are two sides to this gem, publishers and subscribers
26
+
27
+ ### Publishers
28
+
29
+ Publishing to a message is super simple
30
+
31
+ HonestPubsub.publish('user.created', { id: 3, name: 'Foo Bar', email: 'foobar@email.com')
32
+
33
+ Additionally you can setup a context that is passed down to the subscribers. This context can be set up in a `before_filter`
34
+
35
+ ```ruby
36
+ class ApplicationController < ActionController::Base
37
+ before_filter :setup_pubsub_context
38
+
39
+ private
40
+
41
+ def setup_pubsub_context
42
+ HonestPubsub::Context.setup_context
43
+ unique_id: request.uuid,
44
+ orig_ip_address: request.remote_ip,
45
+ application: "my_app/web:#{self.class.name.underscore}/#{action_name}",
46
+ user_id: current_user.try(:id)
47
+ end
48
+ end
49
+ ```
50
+
51
+ Context is automatically cleared up by the gem for a Rails Application. Otherwise, you can call
52
+ `HonestPubsub::Context.clear!`
53
+
54
+ ### Subscribers
55
+
56
+ You can declare a subscriber class as follows
57
+
58
+ ```ruby
59
+ class UserWelcomeSubscriber < HonestPubsub::Server::ClientWorker
60
+ subscribe_to "user_created" # The message prefix that you're subscribing to
61
+ # subscribe_to "user_created", on: 'welcome_emails_queue' # If you want to specify the queue name
62
+
63
+ def perform(context, payload)
64
+ # context is a hash that was put into HonestPubsub::Context
65
+ # Payload contains the data that was published
66
+ # My awesome logic goes here...
67
+ end
68
+ end
69
+ ```
70
+
71
+ You also run subscribers in a separate process to consume the message using the provided executable
72
+
73
+ `bundle exec start_subscribers`
74
+
75
+ You can execute `bundle exec start_subscribers --help` to see all the various options that it providers
76
+ ```
77
+ Usage: bundle exec start_subscribers [options]
78
+ -P, --pidfile PATH path to pidfile
79
+ -o, --only [SUBSCRIBERS] comma separated name of subsriber classes that should be run
80
+ -r, --require [PATH|DIR] Location of Rails application with workers or file to require
81
+ -v, --version Print version and exit
82
+
83
+ ```
84
+
85
+
86
+ ## Contributing
87
+
88
+ 1. Fork it
89
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
90
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
91
+ 4. Push to the branch (`git push origin my-new-feature`)
92
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require_relative '../lib/honest_pubsub/cli'
4
+
5
+ begin
6
+ cli = HonestPubsub::CLI.instance
7
+ cli.parse
8
+ cli.run
9
+ rescue => e
10
+ raise e if $DEBUG
11
+ STDERR.puts e.message
12
+ STDERR.puts e.backtrace.join("\n")
13
+ exit 1
14
+ end
data/config/pubsub.yml ADDED
@@ -0,0 +1,17 @@
1
+ development:
2
+ # Documentation for parameters for rabbit and bunny is located here: http://rubybunny.info/articles/connecting.html
3
+ connection:
4
+ host: localhost
5
+ port: 5672
6
+ # username: rabbit
7
+ # password: rabbit
8
+ heartbeat: 60 # in seconds
9
+ log_level: 0
10
+ log_file: rabbit.log
11
+ network_recovery_interval: 10 # in seconds
12
+ continuation_timeout: 4000 # in milliseconds
13
+
14
+ logger:
15
+ enabled: true
16
+ level: warn
17
+ file: pubsub.log
@@ -0,0 +1,38 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+
5
+ # require all files in the folder?
6
+ require 'honest_pubsub/version'
7
+
8
+ Gem::Specification.new do |spec|
9
+ spec.name = "honest_pubsub"
10
+ spec.version = HonestPubsub::VERSION
11
+ spec.authors = ["Thanh Lim"]
12
+ spec.email = ["thanh@thehonestcompany.com"]
13
+ spec.description = "Pub sub gem for Honest Company"
14
+ spec.summary = "Pub sub gem for Honest Company"
15
+ spec.homepage = ""
16
+ spec.license = "Private"
17
+
18
+
19
+ spec.files = `git ls-files`.split($/)
20
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
21
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
22
+ spec.require_paths = ["lib"]
23
+
24
+ spec.add_development_dependency "bundler", "~> 1.3"
25
+ spec.add_development_dependency "rake", ">= 10.0"
26
+ spec.add_development_dependency "rspec", "~> 3.0.0"
27
+ spec.add_development_dependency "debugger"
28
+ spec.add_runtime_dependency "activesupport", ">= 3.2"
29
+ spec.add_runtime_dependency "awesome_print", "~> 1.2.0"
30
+ spec.add_runtime_dependency "bunny", ">= 1.2"
31
+ spec.add_runtime_dependency "airbrake", ">= 3.1"
32
+ spec.add_runtime_dependency "hashie", ">= 1.2"
33
+ spec.add_runtime_dependency "json", ">= 1.8"
34
+ spec.add_runtime_dependency "celluloid", ">= 0.15"
35
+ spec.add_runtime_dependency "celluloid-io", ">= 0.15"
36
+
37
+
38
+ end
@@ -0,0 +1,94 @@
1
+ # This class handles parsing and running the command line interface for executing subscribers
2
+
3
+ $stdout.sync = true
4
+
5
+ require 'singleton'
6
+ require 'optparse'
7
+ require 'honest_pubsub/server'
8
+
9
+ module HonestPubsub
10
+ class CLI
11
+ include Singleton
12
+
13
+ attr_accessor :pidfile, :subscribers
14
+
15
+ # Method to support parsing of arguments passed through the command line
16
+ def parse(args = ARGV)
17
+ optparse = OptionParser.new do |opts|
18
+ opts.banner = "Usage: bundle exec start_subscribers [options]"
19
+ opts.on '-P', '--pidfile PATH', "path to pidfile" do |arg|
20
+ @pidfile = arg
21
+ end
22
+
23
+ opts.on("-o", "--only [SUBSCRIBERS]", "comma separated name of subsriber classes that should be run") do |subscribers|
24
+ @subscribers = subscribers.split(/\,/)
25
+ end
26
+
27
+ opts.on '-r', '--require [PATH|DIR]', "Location of Rails application with workers or file to require" do |arg|
28
+ @require_path = arg
29
+ end
30
+
31
+ opts.on '-v', '--version', "Print version and exit" do |arg|
32
+ puts "HonestPubsub #{HonestPubsub::VERSION}"
33
+ abort
34
+ end
35
+ end
36
+
37
+ optparse.parse!(args)
38
+ end
39
+
40
+ def run
41
+ load_environment
42
+ write_pidfile
43
+ load_subscribers
44
+ end
45
+
46
+ def require_path
47
+ @require_path || "."
48
+ end
49
+
50
+
51
+ # @return [Array] returns array of subscriber classes that will be executed by the CLI
52
+ def subscriber_classes
53
+ if subscribers.present?
54
+ subscribers.map(&:constantize)
55
+ else
56
+ HonestPubsub::Server::ClientWorker.class_variable_get(:@@registered_subscribers)
57
+ end
58
+ end
59
+
60
+ def remove_pid
61
+ return unless pidfile
62
+ File.delete(pidfile) if File.exist?(pidfile)
63
+ end
64
+
65
+ private
66
+
67
+ def load_environment
68
+ if require_path
69
+ raise ArgumentError, "#{require_path} does not exist" unless File.exist?(require_path)
70
+ end
71
+
72
+ if File.directory?(require_path)
73
+ require 'rails'
74
+ require File.expand_path("#{require_path}/config/environment.rb")
75
+ ::Rails.application.eager_load!
76
+ else
77
+ require require_path
78
+ end
79
+ end
80
+
81
+ def write_pidfile
82
+ return unless pidfile
83
+ File.open(pidfile, 'w') do |f|
84
+ f.puts Process.pid
85
+ end
86
+ end
87
+
88
+ def load_subscribers
89
+ HonestPubsub::Server::SubscriberServer.new(subscriber_classes).start
90
+ end
91
+ end
92
+ end
93
+
94
+
@@ -0,0 +1,38 @@
1
+ module HonestPubsub
2
+ class Configuration
3
+ @@conf = nil
4
+
5
+ def self.configure_with(environment_name, yaml_file = nil)
6
+ @@yaml_file = yaml_file.nil? ? "config/pubsub.yml" : yaml_file
7
+ @@all_conf = Hashie::Mash.new(YAML.load_file(@@yaml_file) )
8
+ @@conf = @@all_conf[environment_name.to_sym]
9
+ self
10
+ end
11
+
12
+ def self.configuration
13
+ self.configure_with(self.environment) if @@conf.nil?
14
+ @@conf
15
+ end
16
+
17
+ def self.environment
18
+ val = ENV["RAILS_ENV"] || ENV["RACK_ENV"]
19
+ val = if val.present?
20
+ val.to_sym
21
+ else
22
+ if defined?(Rails)
23
+ Rails.env
24
+ else
25
+ raise "No environment can be found for configuration!"
26
+ end
27
+ end
28
+ end
29
+
30
+ def self.application_name
31
+ @application_name ||= if defined?(Rails)
32
+ Rails.application.class.name.to_s.gsub("::Application", '')
33
+ else
34
+ 'honest_pubsub'
35
+ end.downcase
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,70 @@
1
+ module HonestPubsub
2
+ class Context
3
+ # acceptable values
4
+ # employee - employee id that the context could have
5
+ # user_id - ender user id of call
6
+ # unique_id - unique identifier (could be request.uuid most of the time)
7
+ # orig_ip_address - ip address of originating requester
8
+ def initialize(params_hash = {})
9
+ @data = Hashie::Mash.new(params_hash)
10
+ end
11
+
12
+ def user_id
13
+ @data.user_id
14
+ end
15
+
16
+ def employee_id
17
+ @data.employee_id
18
+ end
19
+
20
+ def as_json
21
+ @data.as_json
22
+ end
23
+
24
+ def unique_id
25
+ @data.unique_id
26
+ end
27
+
28
+ def originating_ip_address
29
+ @data.orig_ip_address
30
+ end
31
+
32
+ def serialize
33
+ @data.as_json
34
+ end
35
+
36
+ def application
37
+ @data.application
38
+ end
39
+
40
+ def self.instance
41
+ Thread.current['pubsub_context'] || {}
42
+ end
43
+
44
+ def self.setup_context(attrs = {})
45
+ Thread.current['pubsub_context'] = self.new(attrs)
46
+ end
47
+
48
+ def self.clear!
49
+ Thread.current['pubsub_context'] = nil
50
+ end
51
+
52
+ # USE AS INFREQUENTLY AS POSSIBLE. Only use it if you absolutely must
53
+ # as really, we want context in the object, and not the kitchen sink,
54
+ # which is what this will turn out to be if we use regularly use this
55
+ # mechanism.
56
+ def method_missing(method_name, *arguments, &block)
57
+ if @data.key?(method_name)
58
+ @data[method_name]
59
+ else
60
+ super
61
+ end
62
+
63
+ end
64
+
65
+ def self.from_json(data_hash)
66
+ ::HonestPubsub::Context.new(data_hash)
67
+ end
68
+
69
+ end
70
+ end
@@ -0,0 +1,52 @@
1
+
2
+ # Internal database logger that saves the publish message before it is send
3
+ # and also acts as a sink for messages.
4
+ module HonestPubsub
5
+ class DbLogger
6
+
7
+ def initialize
8
+ @config = Configuration.configuration[:mongo]
9
+ if @config.present?
10
+ @client = Mongo::MongoClient.new(@config[:host], @config[:port])
11
+ else
12
+ @client = Mongo::MongoClient.new
13
+ end
14
+
15
+ @db = @client.db("logger")
16
+
17
+ end
18
+
19
+ def start_subscribe
20
+ subscriber = ::HonestPubsub::Subscriber.new("")
21
+ subscriber.start("logger") do |info, properties, contents|
22
+ # Write to mongo the contents and the routing_key, with the routing_key being indexed
23
+ routing_key = info[:routing_key]
24
+ chunks = routing_key.split(".")
25
+ # Logger will split the data into collections based upon the first parameter of the routing_key (initial roll out will only use 'honest')
26
+ collection_name = chunks[1] || chunks[0]
27
+ collection = @db.collection(collection_name)
28
+ # no need for safe writes. need to write to a file log as well.
29
+ collection.insert({routing_key: routing_key, cts: contents, ts: properties[:timestamp]})
30
+ # make sure that we always index these two items in mongo
31
+ # it's a small price of a call, so no big deal
32
+ collection.ensure_index( {routing_key:1, ts:1} )
33
+ end
34
+ end
35
+
36
+ def log_publish(routing_key, message)
37
+ collection_name = "publisher"
38
+ collection = @db.collection(collection_name)
39
+
40
+ # no need for safe writes. need to write to a file log as well.
41
+ collection.insert({routing_key: routing_key, cts: message[:payload], ts: message[:ts], pub: message[:pub]})
42
+ # make sure that we always index these two items in mongo
43
+ # it's a small price of a call, so no big deal
44
+ collection.ensure_index( {routing_key:1, ts:1} )
45
+
46
+ end
47
+
48
+ def teardown
49
+ @subscriber.teardown
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,2 @@
1
+ class PayloadError < StandardError
2
+ end
@@ -0,0 +1,49 @@
1
+ # Internal log class that is used to log messages before sending, after receiving, and failure to send
2
+ module HonestPubsub
3
+ class Logger
4
+ def initialize(enable_publish_logging = true)
5
+ @enabled = HonestPubsub::Configuration.configuration[:logger][:enabled]
6
+ @enabled = enable_publish_logging if @enabled.nil?
7
+ end
8
+
9
+ def log_publish(routing_key, message)
10
+ return unless @enabled
11
+ # generate tags needed
12
+ tags = ["PUBLISH", message[:ts], message[:pub], message[:v], routing_key]
13
+
14
+ # FIX!!! -thl
15
+ # Could logging like this be too slow?
16
+ # Or should it be threaded?
17
+ # We'll need to benchmark, as we don't want this to get too slow.
18
+ logger.tagged(tags) { logger.warn message[:payload].as_json }
19
+
20
+ # TODO: -thl
21
+ # Log it into mongo as well?
22
+ end
23
+
24
+ def failed_publish(routing_key, properties, message)
25
+ tags = ["FAILED_SEND", message[:ts], message[:pub], message[:v], routing_key]
26
+ logger.tagged(tags) { logger.warn message[:payload].as_json }
27
+ end
28
+
29
+ def log_receive(routing_key, message)
30
+ tags = ["RECEIVED", message[:ts], message[:pub], message[:v], routing_key, Process::pid]
31
+ logger.tagged(tags) { logger.warn message[:payload].as_json }
32
+ end
33
+
34
+ def log_service(service_name, log_level, message)
35
+ tags = ["SERVICE", service_name, Process::pid]
36
+ log_method = log_level.to_sym.to_proc
37
+ logger.tagged(tags) { log_method.call(logger) { message.as_json } }
38
+ end
39
+
40
+ def teardown
41
+ logger.close
42
+ end
43
+
44
+ private
45
+ def logger
46
+ @logger ||= ActiveSupport::TaggedLogging.new(HonestPubsub.logger)
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,42 @@
1
+ require 'logger'
2
+
3
+ # Manages a logger that can be used throughout the pubsub gem
4
+ # The logger can be set to any object that quacks like a logger including the
5
+ # Rails logger if so desired
6
+ #
7
+ # The logger can be set in the initializer of a program or anywhere throughout as
8
+ # HonestPubsub.logger = Rails.logger for example
9
+
10
+ module HonestPubsub
11
+ module Logging
12
+ # Sets the logger
13
+ #
14
+ # @param logger The logger to use throughout the pubsub gem
15
+ # @return The logger we use throughout the gem
16
+ def self.logger=(logger)
17
+ raise StandardError("Can't set logger to nil") unless logger.present?
18
+ @logger = logger
19
+ end
20
+
21
+ # Gets the logger
22
+ # If no logger is defined when this method is called it will return a standard
23
+ # ruby logger
24
+ #
25
+ # @return The logger we use throughout the gem
26
+ def self.logger
27
+ config = HonestPubsub::Configuration.configuration[:logger]
28
+ @logger ||= create_logger( {}.
29
+ merge( config[:level].present? ? { log_level: ::Logger::Severity.const_get(config[:level].upcase) } : {} ).
30
+ merge( config[:file].present? ? { log_target: config[:file] } : {} ) )
31
+ end
32
+
33
+ # Builds a standard ruby Logger
34
+ #
35
+ # @return Returns the logger at the end of the method
36
+ def self.create_logger( options = {} )
37
+ @logger = ::Logger.new(options.fetch(:log_target){ STDOUT })
38
+ @logger.level = options.fetch(:log_level){ ::Logger::INFO }
39
+ @logger
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,50 @@
1
+
2
+ require "hashie"
3
+ require "json"
4
+
5
+ module HonestPubsub
6
+ class Message
7
+ @@message_version = "1"
8
+ attr_reader :ts
9
+ attr_reader :pub
10
+ attr_reader :version
11
+ attr_reader :payload
12
+ attr_reader :context
13
+
14
+ def initialize
15
+ end
16
+
17
+ # Context object should be passed into the call.
18
+ def serialize(context, routing_key, payload)
19
+ @ts = Time.now.to_i
20
+ @pub = "#{routing_key}:#{Socket.gethostname()}:#{Process::pid}"
21
+ @version = @@message_version
22
+ @payload = payload
23
+ @context = context
24
+ to_hash
25
+ end
26
+
27
+ def parse(envelope)
28
+ contents = Hashie::Mash.new(::JSON.parse(envelope))
29
+ @ts = contents[:ts]
30
+ @pub = contents[:pub]
31
+ # Version used for messaging can get updated if we need to add extra header information, and need
32
+ # to move each section of the library separately.
33
+ @version = contents[:v]
34
+ @payload = contents[:payload]
35
+ @context = ::HonestPubsub::Context.from_json(contents[:context])
36
+ to_hash
37
+ end
38
+
39
+ def to_hash
40
+ ::Hashie::Mash.new({
41
+ ts: @ts,
42
+ pub: @pub,
43
+ v: @@message_version,
44
+ payload: @payload,
45
+ context: @context.as_json
46
+ })
47
+ end
48
+
49
+ end
50
+ end
@@ -0,0 +1,14 @@
1
+ # Middleware to ensure that we clear the context with every request
2
+
3
+ module HonestPubsub
4
+ class Middleware
5
+ def initialize(app)
6
+ @app = app
7
+ end
8
+
9
+ def call(env)
10
+ HonestPubsub::Context.clear!
11
+ @app.call(env)
12
+ end
13
+ end
14
+ end