demux 0.1.0.beta

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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 24735d1c05b065cef6a590c511594ad2e4aee7c51218debb1eb1cfbc859fa50b
4
+ data.tar.gz: 65d933eb7a814ed3da0125d0c313f90b4dc4994ff743c4b8b3b2f434fdc50941
5
+ SHA512:
6
+ metadata.gz: 922f311246e08d28291199fc61955a710a9d5f5fa6c7fa4f979bfa946f947569933c5bbd152690af011df311d29f156d7138be2db814e31ce9fecd261c1d03d5
7
+ data.tar.gz: e40331c445703f6dfc8753bb8399084ef5f694e92e194761b283bd91d45587df0dcb0e94e80969035cf9f5a50fcab602ac63940518b000e5e46d7c814da53400
@@ -0,0 +1,20 @@
1
+ Copyright 2020 Ross Reinhardt
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,36 @@
1
+ [![Build Status](https://travis-ci.com/rreinhardt9/demux.svg?branch=master)](https://travis-ci.com/rreinhardt9/demux)
2
+
3
+ # Demux
4
+ Short description and motivation.
5
+
6
+ ## Usage
7
+ How to use my plugin.
8
+
9
+ ## Installation
10
+ Add this line to your application's Gemfile:
11
+
12
+ ```ruby
13
+ gem 'demux'
14
+ ```
15
+
16
+ And then execute:
17
+ ```bash
18
+ $ bundle
19
+ ```
20
+
21
+ Or install it yourself as:
22
+ ```bash
23
+ $ gem install demux
24
+ ```
25
+
26
+ ## Contributing
27
+ After cloning repo:
28
+
29
+ - install gems `bundle install`
30
+ - set up the databases `bundle exec rake db:setup`
31
+ - If you run into trouble setting up databases because of a missing postgres role, you can create one by running `psql` and then running `ALTER ROLE postgres LOGIN CREATEDB;`
32
+ - If you cannot start `psql` because you are missing a database named after your local user, you can create one using `createdb`
33
+ - You should not be able to run the tests `bundle exec rake`
34
+
35
+ ## License
36
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
@@ -0,0 +1,32 @@
1
+ begin
2
+ require 'bundler/setup'
3
+ rescue LoadError
4
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
5
+ end
6
+
7
+ require 'rdoc/task'
8
+
9
+ RDoc::Task.new(:rdoc) do |rdoc|
10
+ rdoc.rdoc_dir = 'rdoc'
11
+ rdoc.title = 'Demux'
12
+ rdoc.options << '--line-numbers'
13
+ rdoc.rdoc_files.include('README.md')
14
+ rdoc.rdoc_files.include('lib/**/*.rb')
15
+ end
16
+
17
+ APP_RAKEFILE = File.expand_path("test/dummy/Rakefile", __dir__)
18
+ load 'rails/tasks/engine.rake'
19
+
20
+ load 'rails/tasks/statistics.rake'
21
+
22
+ require 'bundler/gem_tasks'
23
+
24
+ require 'rake/testtask'
25
+
26
+ Rake::TestTask.new(:test) do |t|
27
+ t.libs << 'test'
28
+ t.pattern = 'test/**/*_test.rb'
29
+ t.verbose = false
30
+ end
31
+
32
+ task default: :test
@@ -0,0 +1 @@
1
+ //= link_directory ../stylesheets/demux .css
@@ -0,0 +1,15 @@
1
+ /*
2
+ * This is a manifest file that'll be compiled into application.css, which will include all the files
3
+ * listed below.
4
+ *
5
+ * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
6
+ * or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path.
7
+ *
8
+ * You're free to add application-wide styles to this file and they'll appear at the bottom of the
9
+ * compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS
10
+ * files in this directory. Styles in this file should be added after the last require_* statement.
11
+ * It is generally better to create a new file per style scope.
12
+ *
13
+ *= require_tree .
14
+ *= require_self
15
+ */
@@ -0,0 +1,5 @@
1
+ module Demux
2
+ class ApplicationController < ActionController::Base
3
+ protect_from_forgery with: :exception
4
+ end
5
+ end
@@ -0,0 +1,4 @@
1
+ module Demux
2
+ module ApplicationHelper
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module Demux
2
+ class ApplicationJob < ActiveJob::Base
3
+ end
4
+ end
@@ -0,0 +1,6 @@
1
+ module Demux
2
+ class ApplicationMailer < ActionMailer::Base
3
+ default from: 'from@example.com'
4
+ layout 'mailer'
5
+ end
6
+ end
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "jwt"
4
+
5
+ module Demux
6
+ # Demux::App represents an external app that can be connected to an account
7
+ # in the parent application.
8
+ class App < ApplicationRecord
9
+ URL_REGEX = %r{\A(http(s?)\://.+)?\z}i.freeze
10
+
11
+ has_many :connections
12
+ has_many :transmissions
13
+ has_secure_token :secret
14
+
15
+ validates :entry_url, :signal_url, format: { with: URL_REGEX }
16
+
17
+ validates :name, presence: true
18
+
19
+ class << self
20
+ def listening_for(signal_name:, account_id:)
21
+ connections = Demux::Connection.listening_for(
22
+ signal_name: signal_name,
23
+ account_id: account_id
24
+ )
25
+
26
+ joins(:connections)
27
+ .merge(connections)
28
+ .where.not(signal_url: nil)
29
+ end
30
+
31
+ def without_queued_transmissions_for(signal_hash)
32
+ joins(
33
+ <<~SQL
34
+ LEFT OUTER JOIN demux_transmissions
35
+ ON demux_transmissions.app_id = demux_apps.id
36
+ AND demux_transmissions.status = 0
37
+ AND demux_transmissions.uniqueness_hash = '#{signal_hash}'
38
+ SQL
39
+ )
40
+ .where(demux_transmissions: { id: nil })
41
+ end
42
+
43
+ def transmission_requested_all(signal_attributes)
44
+ without_queued_transmissions_for(signal_attributes.hashed).each do |app|
45
+ app.transmission_requested(signal_attributes)
46
+ end
47
+ end
48
+ end
49
+
50
+ def transmission_requested(signal_attributes)
51
+ transmissions.queue(signal_attributes)
52
+ end
53
+
54
+ # Return an entry url with JWT payload for authorization
55
+ #
56
+ # @param data [Hash] data to sign and include in token payload
57
+ # @param exp [Integer] expiration of token in seconds since the epoch
58
+ #
59
+ # @return [String] the entry url with signed token appended
60
+
61
+ def signed_entry_url(data: {}, exp: 1.minute.from_now.to_i)
62
+ token = JWT.encode({ data: data, exp: exp }, secret, "HS256")
63
+ "#{entry_url}?token=#{token}"
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,5 @@
1
+ module Demux
2
+ class ApplicationRecord < ActiveRecord::Base
3
+ self.abstract_class = true
4
+ end
5
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Demux
4
+ class Connection < ApplicationRecord
5
+ belongs_to :app
6
+
7
+ class << self
8
+ def listening_for(signal_name:, account_id:)
9
+ where(account_id: account_id)
10
+ .signal(signal_name)
11
+ .or(wildcard_signal)
12
+ end
13
+
14
+ def signal(signal)
15
+ where("demux_connections.signals @> ?", "{#{signal}}")
16
+ end
17
+
18
+ private
19
+
20
+ def wildcard_signal
21
+ where("demux_connections.signals @> ?", "{*}")
22
+ end
23
+ end
24
+
25
+ # Return an entry url for this specific connection
26
+ #
27
+ # @return [String] the entry url with account_id in signed token
28
+
29
+ def entry_url
30
+ app.signed_entry_url(data: { account_id: account_id })
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,81 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Demux
4
+ # Demux::Transmission represents a signal being sent to an app
5
+ class Transmission < ApplicationRecord
6
+ belongs_to :app
7
+
8
+ before_save :update_uniqueness_hash
9
+
10
+ enum status: %i[queued sending success failure]
11
+
12
+ class << self
13
+ def for_app(app_relation)
14
+ joins(:app).merge(app_relation)
15
+ end
16
+
17
+ def queue(signal_attributes)
18
+ create(signal_attributes.to_hash)
19
+ rescue ActiveRecord::RecordNotUnique
20
+ # Unique index by status/uniqueness_hash
21
+ end
22
+ end
23
+
24
+ def transmit
25
+ return self unless attributes_required_to_transmit_present?
26
+
27
+ update(
28
+ request_url: app.signal_url,
29
+ request_body: payload.to_json,
30
+ status: :sending
31
+ )
32
+
33
+ save_receipt(Transmitter.new(self).transmit.receipt)
34
+
35
+ self
36
+ end
37
+
38
+ def save_receipt(receipt)
39
+ update(
40
+ status: receipt.success? ? :success : :failure,
41
+ response_code: receipt.http_code,
42
+ response_body: receipt.response_body,
43
+ request_headers: receipt.request_headers
44
+ )
45
+ end
46
+
47
+ def signal_name
48
+ signal.signal_name
49
+ end
50
+
51
+ def signature
52
+ OpenSSL::HMAC.hexdigest("SHA256", app.secret, request_body)
53
+ end
54
+
55
+ private
56
+
57
+ def signal_url
58
+ app.signal_url
59
+ end
60
+
61
+ def payload
62
+ @payload ||= { action: action }.merge(signal.payload_for(action))
63
+ end
64
+
65
+ def signal
66
+ @signal ||= signal_class.constantize.new(
67
+ object_id, account_id: account_id
68
+ )
69
+ end
70
+
71
+ def update_uniqueness_hash
72
+ return unless attributes_required_to_transmit_present?
73
+
74
+ self.uniqueness_hash = SignalAttributes.from_object(self).hashed
75
+ end
76
+
77
+ def attributes_required_to_transmit_present?
78
+ account_id? && action? && object_id? && signal_class?
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,15 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>Demux</title>
5
+ <%= csrf_meta_tags %>
6
+ <%= csp_meta_tag %>
7
+
8
+ <%= stylesheet_link_tag "demux/application", media: "all" %>
9
+ </head>
10
+ <body>
11
+
12
+ <%= yield %>
13
+
14
+ </body>
15
+ </html>
File without changes
@@ -0,0 +1,2 @@
1
+ Demux::Engine.routes.draw do
2
+ end
@@ -0,0 +1,16 @@
1
+ class CreateDemuxApps < ActiveRecord::Migration[5.1]
2
+ def change
3
+ create_table :demux_apps do |t|
4
+ t.string :name
5
+ t.text :description
6
+ t.string :secret
7
+ t.string :entry_url
8
+ t.string :signal_url
9
+ t.text :signals, array:true, default: []
10
+
11
+ t.timestamps
12
+ end
13
+ add_index :demux_apps, :signals, using: "gin"
14
+ add_index :demux_apps, :secret, unique: true
15
+ end
16
+ end
@@ -0,0 +1,14 @@
1
+ class CreateDemuxConnections < ActiveRecord::Migration[5.1]
2
+ def change
3
+ create_table :demux_connections do |t|
4
+ t.integer :account_id
5
+ t.integer :app_id
6
+ t.text :signals, array:true, default: []
7
+
8
+ t.timestamps
9
+ end
10
+ add_index :demux_connections, :signals, using: "gin"
11
+ add_index :demux_connections, :account_id
12
+ add_index :demux_connections, :app_id
13
+ end
14
+ end
@@ -0,0 +1,23 @@
1
+ class CreateDemuxTransmissions < ActiveRecord::Migration[5.2]
2
+ def change
3
+ create_table :demux_transmissions do |t|
4
+ t.string :signal_class
5
+ t.string :action
6
+ t.integer :object_id
7
+ t.integer :app_id
8
+ t.integer :account_id
9
+ t.integer :status, default: 0
10
+ t.string :response_code
11
+ t.jsonb :response_headers
12
+ t.text :response_body
13
+ t.jsonb :request_headers
14
+ t.text :request_body
15
+ t.string :request_url
16
+ t.string :uniqueness_hash
17
+
18
+ t.timestamps
19
+ end
20
+ add_index :demux_transmissions, :app_id
21
+ add_index :demux_transmissions, [:uniqueness_hash, :app_id], unique: true, where: "status = 0"
22
+ end
23
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "demux/engine"
4
+ require "demux/demuxer"
5
+ require "demux/signal"
6
+ require "demux/signal_attributes"
7
+ require "demux/transmitter"
8
+
9
+ # Demux toplevel namespace
10
+ module Demux
11
+ # Access the current configuration
12
+
13
+ module_function
14
+
15
+ def configuration
16
+ @configuration ||= Configuration.new
17
+ end
18
+
19
+ # Alias so that we can refer to configuration as config
20
+
21
+ def config
22
+ configuration
23
+ end
24
+
25
+ # Configure the library
26
+ #
27
+ # @yieldparam [Demux::Configuration] current_configuration
28
+ #
29
+ # @example
30
+ # Demux.configure do |config|
31
+ # config.default_demuxer = "Demux::Demuxer"
32
+ # end
33
+ #
34
+ # @yieldreturn [Demux::Configuration]
35
+
36
+ def configure
37
+ yield configuration
38
+ end
39
+
40
+ # Configuration holds the current configuration for the SeisimicAPI
41
+ # and provides defaults
42
+ class Configuration
43
+ attr_accessor :default_demuxer
44
+
45
+ def initialize(args = {})
46
+ @default_demuxer = args.fetch(:default_demuxer) { Demux::Demuxer }
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Demux
4
+ # Demux::Demuxer is the heart of pairing signals to apps.
5
+ # It's a base implementation of what needs to happen, but apps can
6
+ # provide their own custom demuxer that calls out to this one.
7
+ #
8
+ # For example a host application will likely want to process signals in a
9
+ # background queue and can supply their own demuxer with details on how that
10
+ # should happen.
11
+ class Demuxer
12
+ def initialize(signal_attributes)
13
+ @signal_attributes = signal_attributes
14
+ @account_id = @signal_attributes.account_id
15
+ @signal_class = @signal_attributes.signal_class
16
+ end
17
+
18
+ def send_to_apps
19
+ queue_transmissions
20
+
21
+ queued_transmissions.each(&:transmit)
22
+
23
+ self
24
+ end
25
+
26
+ def queued_transmissions
27
+ Transmission
28
+ .queued
29
+ .for_app(listening_apps)
30
+ .where(uniqueness_hash: @signal_attributes.hashed)
31
+ end
32
+
33
+ def listening_apps
34
+ Demux::App.listening_for(
35
+ signal_name: @signal_class.constantize.signal_name,
36
+ account_id: @account_id
37
+ )
38
+ end
39
+
40
+ def queue_transmissions
41
+ listening_apps.transmission_requested_all(@signal_attributes)
42
+
43
+ self
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,5 @@
1
+ module Demux
2
+ class Engine < ::Rails::Engine
3
+ isolate_namespace Demux
4
+ end
5
+ end
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Demux
4
+ # All signals will inherit from Demux::Signal. A signal represent a message
5
+ # to be demuxed and sent to apps.
6
+ class Signal
7
+ attr_reader :account_id
8
+
9
+ class << self
10
+ attr_reader :object_class, :signal_name
11
+
12
+ def attributes(attr)
13
+ @object_class = attr.fetch(:object_class)
14
+ @signal_name = attr.fetch(:signal_name)
15
+ end
16
+ end
17
+
18
+ def signal_name
19
+ self.class.signal_name
20
+ end
21
+
22
+ def initialize(object_id,
23
+ account_id:,
24
+ demuxer: Demux.config.default_demuxer)
25
+ @object_id = Integer(object_id)
26
+ @account_id = account_id
27
+ @demuxer = demuxer
28
+ end
29
+
30
+ def object
31
+ @object ||= self.class.object_class.find(@object_id)
32
+ end
33
+
34
+ def payload_for(action)
35
+ if respond_to?("#{action}_payload")
36
+ public_send("#{action}_payload")
37
+ else
38
+ payload
39
+ end
40
+ end
41
+
42
+ def send(action)
43
+ @demuxer.new(
44
+ SignalAttributes.new(
45
+ account_id: @account_id,
46
+ action: String(action),
47
+ object_id: @object_id,
48
+ signal_class: self.class.name
49
+ )
50
+ ).send_to_apps
51
+
52
+ self
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Demux
4
+ # Attributes that are commonly used to identify a signal
5
+ class SignalAttributes
6
+ attr_reader :account_id, :action, :object_id, :signal_class
7
+
8
+ class << self
9
+ def from_object(object)
10
+ new(
11
+ account_id: object.account_id,
12
+ action: object.action,
13
+ object_id: object.object_id,
14
+ signal_class: object.signal_class
15
+ )
16
+ end
17
+ end
18
+
19
+ def initialize(account_id:, action:, object_id:, signal_class:)
20
+ @account_id = account_id
21
+ @action = action
22
+ @object_id = object_id
23
+ @signal_class = String(signal_class)
24
+ end
25
+
26
+ def to_hash
27
+ {
28
+ account_id: @account_id,
29
+ action: @action,
30
+ object_id: @object_id,
31
+ signal_class: @signal_class
32
+ }
33
+ end
34
+
35
+ def hashed
36
+ Base64.strict_encode64({
37
+ account_id: account_id,
38
+ action: action,
39
+ object_id: object_id,
40
+ signal_class: signal_class
41
+ }.to_json)
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,136 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "net/http"
4
+
5
+ module Demux
6
+ # Transmit a Transmission
7
+ class Transmitter
8
+ attr_reader :receipt
9
+ # Constructor
10
+ #
11
+ # @param transmission [Demux::Transmission] the transmission to be sent
12
+ #
13
+ # @return [self] the initialized Demux::Transmitter
14
+
15
+ def initialize(transmission)
16
+ @transmission = transmission
17
+ @uri = URI(@transmission.request_url)
18
+ @receipt = NullTransmissionReceipt.new
19
+ end
20
+
21
+ # Use the transmitter to send it's transmission
22
+ #
23
+ # @return [self]
24
+
25
+ def transmit
26
+ build_request
27
+
28
+ send_request
29
+
30
+ @receipt = TransmissionReceipt.new(@request, @response)
31
+
32
+ log_transmission
33
+
34
+ self
35
+ end
36
+
37
+ private
38
+
39
+ def build_request
40
+ @request = Net::HTTP::Post.new(@uri).tap do |request|
41
+ request["X-Demux-Signal"] = @transmission.signal_name
42
+ request["X-Demux-Signature"] = @transmission.signature
43
+ request["Content-Type"] = "application/json"
44
+ request["User-Agent"] = "Demux"
45
+ request.body = @transmission.request_body
46
+ end
47
+ end
48
+
49
+ def send_request
50
+ @response =
51
+ Net::HTTP.start(@uri.hostname, @uri.port, use_ssl: true) do |http|
52
+ http.request(@request)
53
+ end
54
+ end
55
+
56
+ def log_transmission
57
+ Rails.logger.debug(
58
+ "Send #{@transmission.signal_name}/#{@transmission.action} signal \
59
+ to #{@uri} \
60
+ with payload #{@transmission.request_body}"
61
+ )
62
+ end
63
+ end
64
+
65
+ # Null object to represent having no receipt
66
+ class NullTransmissionReceipt
67
+ def success?
68
+ nil
69
+ end
70
+
71
+ def http_code
72
+ nil
73
+ end
74
+
75
+ def request_headers
76
+ {}
77
+ end
78
+
79
+ def request_body
80
+ ""
81
+ end
82
+
83
+ def response_body
84
+ ""
85
+ end
86
+ end
87
+
88
+ # Returned when the Transmitter transmits
89
+ # @see Demux::Transmitter
90
+ class TransmissionReceipt
91
+ def initialize(request, response)
92
+ @raw_request = request
93
+ @raw_response = response
94
+ end
95
+
96
+ # Was the response code 2xx
97
+ #
98
+ # @return [Boolean]
99
+
100
+ def success?
101
+ @raw_response.is_a?(Net::HTTPSuccess)
102
+ end
103
+
104
+ # HTTP code of response
105
+ #
106
+ # @return [Integer] http code
107
+
108
+ def http_code
109
+ Integer(@raw_response.code)
110
+ end
111
+
112
+ # Headers that were sent with request
113
+ #
114
+ # @return [Hash] Hash of headers
115
+
116
+ def request_headers
117
+ @raw_request.each_header.to_h
118
+ end
119
+
120
+ # Body of the request
121
+ #
122
+ # @return [String] request body
123
+
124
+ def request_body
125
+ @raw_request.body
126
+ end
127
+
128
+ # Body of the response
129
+ #
130
+ # @return [String] response body
131
+
132
+ def response_body
133
+ @raw_response.body
134
+ end
135
+ end
136
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Demux
4
+ VERSION = "0.1.0.beta"
5
+ end
@@ -0,0 +1,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :demux do
3
+ # # Task goes here
4
+ # end
metadata ADDED
@@ -0,0 +1,139 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: demux
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0.beta
5
+ platform: ruby
6
+ authors:
7
+ - Ross Reinhardt
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2020-05-29 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: jwt
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '1.5'
20
+ - - "<"
21
+ - !ruby/object:Gem::Version
22
+ version: '3'
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ version: '1.5'
30
+ - - "<"
31
+ - !ruby/object:Gem::Version
32
+ version: '3'
33
+ - !ruby/object:Gem::Dependency
34
+ name: rails
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: '5.1'
40
+ - - "<"
41
+ - !ruby/object:Gem::Version
42
+ version: '7'
43
+ type: :runtime
44
+ prerelease: false
45
+ version_requirements: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - ">="
48
+ - !ruby/object:Gem::Version
49
+ version: '5.1'
50
+ - - "<"
51
+ - !ruby/object:Gem::Version
52
+ version: '7'
53
+ - !ruby/object:Gem::Dependency
54
+ name: pg
55
+ requirement: !ruby/object:Gem::Requirement
56
+ requirements:
57
+ - - ">="
58
+ - !ruby/object:Gem::Version
59
+ version: '0'
60
+ type: :development
61
+ prerelease: false
62
+ version_requirements: !ruby/object:Gem::Requirement
63
+ requirements:
64
+ - - ">="
65
+ - !ruby/object:Gem::Version
66
+ version: '0'
67
+ - !ruby/object:Gem::Dependency
68
+ name: webmock
69
+ requirement: !ruby/object:Gem::Requirement
70
+ requirements:
71
+ - - ">="
72
+ - !ruby/object:Gem::Version
73
+ version: '0'
74
+ type: :development
75
+ prerelease: false
76
+ version_requirements: !ruby/object:Gem::Requirement
77
+ requirements:
78
+ - - ">="
79
+ - !ruby/object:Gem::Version
80
+ version: '0'
81
+ description: Configure your application to send signals to a constellation of other
82
+ 'apps'
83
+ email:
84
+ - rreinhardt9@gmail.com
85
+ executables: []
86
+ extensions: []
87
+ extra_rdoc_files: []
88
+ files:
89
+ - MIT-LICENSE
90
+ - README.md
91
+ - Rakefile
92
+ - app/assets/config/demux_manifest.js
93
+ - app/assets/stylesheets/demux/application.css
94
+ - app/controllers/demux/application_controller.rb
95
+ - app/helpers/demux/application_helper.rb
96
+ - app/jobs/demux/application_job.rb
97
+ - app/mailers/demux/application_mailer.rb
98
+ - app/models/demux/app.rb
99
+ - app/models/demux/application_record.rb
100
+ - app/models/demux/connection.rb
101
+ - app/models/demux/transmission.rb
102
+ - app/views/layouts/demux/application.html.erb
103
+ - config/environment.rb
104
+ - config/routes.rb
105
+ - db/migrate/20200423143645_create_demux_apps.rb
106
+ - db/migrate/20200423144102_create_demux_connections.rb
107
+ - db/migrate/20200505201706_create_demux_transmissions.rb
108
+ - lib/demux.rb
109
+ - lib/demux/demuxer.rb
110
+ - lib/demux/engine.rb
111
+ - lib/demux/signal.rb
112
+ - lib/demux/signal_attributes.rb
113
+ - lib/demux/transmitter.rb
114
+ - lib/demux/version.rb
115
+ - lib/tasks/demux_tasks.rake
116
+ homepage: https://github.com/rreinhardt9/demux
117
+ licenses:
118
+ - MIT
119
+ metadata: {}
120
+ post_install_message:
121
+ rdoc_options: []
122
+ require_paths:
123
+ - lib
124
+ required_ruby_version: !ruby/object:Gem::Requirement
125
+ requirements:
126
+ - - ">="
127
+ - !ruby/object:Gem::Version
128
+ version: '0'
129
+ required_rubygems_version: !ruby/object:Gem::Requirement
130
+ requirements:
131
+ - - ">"
132
+ - !ruby/object:Gem::Version
133
+ version: 1.3.1
134
+ requirements: []
135
+ rubygems_version: 3.0.3
136
+ signing_key:
137
+ specification_version: 4
138
+ summary: Configure your application to send signals to a constellation of other 'apps'
139
+ test_files: []