honest_pubsub 0.2.2

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.
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