tavern 0.0.1
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.
- data/.gitignore +17 -0
- data/.rspec +1 -0
- data/Gemfile +4 -0
- data/LICENSE +22 -0
- data/README.md +29 -0
- data/Rakefile +17 -0
- data/lib/tavern/hub.rb +117 -0
- data/lib/tavern/mock_hub.rb +54 -0
- data/lib/tavern/subscription.rb +34 -0
- data/lib/tavern/subscriptions.rb +79 -0
- data/lib/tavern/version.rb +3 -0
- data/lib/tavern.rb +6 -0
- data/spec/hub_spec.rb +115 -0
- data/spec/spec_helper.rb +8 -0
- data/tavern.gemspec +22 -0
- metadata +127 -0
data/.gitignore
ADDED
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--colour
|
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2012 Darcy Laycock
|
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,29 @@
|
|
1
|
+
# Tavern
|
2
|
+
|
3
|
+
TODO: Write a gem description
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
gem 'tavern'
|
10
|
+
|
11
|
+
And then execute:
|
12
|
+
|
13
|
+
$ bundle
|
14
|
+
|
15
|
+
Or install it yourself as:
|
16
|
+
|
17
|
+
$ gem install tavern
|
18
|
+
|
19
|
+
## Usage
|
20
|
+
|
21
|
+
TODO: Write usage instructions here
|
22
|
+
|
23
|
+
## Contributing
|
24
|
+
|
25
|
+
1. Fork it
|
26
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
27
|
+
3. Commit your changes (`git commit -am 'Added some feature'`)
|
28
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
29
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
#!/usr/bin/env rake
|
2
|
+
require "bundler/gem_tasks"
|
3
|
+
require 'rake'
|
4
|
+
require 'rspec/core'
|
5
|
+
require 'rspec/core/rake_task'
|
6
|
+
require 'bundler/gem_tasks'
|
7
|
+
|
8
|
+
task :default => :spec
|
9
|
+
|
10
|
+
begin
|
11
|
+
require 'ci/reporter/rake/rspec'
|
12
|
+
rescue LoadError
|
13
|
+
end
|
14
|
+
|
15
|
+
|
16
|
+
desc "Run all specs in spec directory (excluding plugin specs)"
|
17
|
+
RSpec::Core::RakeTask.new :spec
|
data/lib/tavern/hub.rb
ADDED
@@ -0,0 +1,117 @@
|
|
1
|
+
require 'tavern/subscription'
|
2
|
+
require 'tavern/subscriptions'
|
3
|
+
require 'active_support/core_ext/module/delegation'
|
4
|
+
require 'active_support/core_ext/object/blank'
|
5
|
+
|
6
|
+
module Tavern
|
7
|
+
|
8
|
+
class << self
|
9
|
+
|
10
|
+
# Gets the current application wide hub, initializing
|
11
|
+
# a new one if it hasn't been set yet.
|
12
|
+
# @return [Hub] the subscription hub
|
13
|
+
def hub
|
14
|
+
@hub ||= Hub.new.tap { |h| h.primary = true }
|
15
|
+
end
|
16
|
+
|
17
|
+
def hub=(value)
|
18
|
+
old_hub = @hub
|
19
|
+
@hub = value.presence
|
20
|
+
if old_hub != @hub
|
21
|
+
old_hub.primary = false if old_hub
|
22
|
+
@hub.primary = true if @hub
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# We delegate the subscription management methods to the default hub.
|
27
|
+
# Note: This does not replace having an application-wide hub which is still
|
28
|
+
# a good idea.
|
29
|
+
delegate :subscribe, :unsubscribe, :publish, :to => :hub
|
30
|
+
|
31
|
+
end
|
32
|
+
|
33
|
+
# Implements a simplified Pub / Sub hub for in-application notifications.
|
34
|
+
# Used inside smeghead as a general replacement for observers and a way
|
35
|
+
# for items to hook into events.
|
36
|
+
class Hub
|
37
|
+
|
38
|
+
attr_reader :subscriptions
|
39
|
+
|
40
|
+
# Initializes the given hub with an empty set of subscriptions.
|
41
|
+
def initialize
|
42
|
+
@subscriptions = Subscriptions.new
|
43
|
+
@primary = false
|
44
|
+
end
|
45
|
+
|
46
|
+
# Subscribes to a given path string and either a proc callback or
|
47
|
+
# any object responding to #call.
|
48
|
+
# @param [String] path the subscription path
|
49
|
+
# @param [#call] object if present, the callback to invoke
|
50
|
+
# @param [Proc] blk the block to use for the callback (if the object is nil)
|
51
|
+
# @example Subscribing with a block
|
52
|
+
# hub.subscribe 'hello:world' do |ctx|
|
53
|
+
# puts "Context is #{ctx.inspect}"
|
54
|
+
# end
|
55
|
+
# @example Subscribing with an object
|
56
|
+
# hub.subscribe 'hello:world', MyCallableClass
|
57
|
+
def subscribe(path, object = nil, &blk)
|
58
|
+
if object and not object.respond_to?(:call)
|
59
|
+
raise ArgumentError, "you provided an object as an argument but it doesn't respond to #call"
|
60
|
+
end
|
61
|
+
subscription = Subscription.new(path, (object || blk))
|
62
|
+
level = subscriptions.sublevel_at subscription.to_subscribe_keys
|
63
|
+
level.add subscription
|
64
|
+
subscription
|
65
|
+
end
|
66
|
+
|
67
|
+
# Deletes the given subscription from this pub sub hub.
|
68
|
+
# @param [Subscription] subscription the subscription to delete
|
69
|
+
# @return [Subscription] the deleted subscription
|
70
|
+
def unsubscribe(subscription)
|
71
|
+
return if subscription.blank?
|
72
|
+
level = subscriptions.sublevel_at subscription.to_subscribe_keys
|
73
|
+
level.delete subscription
|
74
|
+
subscription
|
75
|
+
end
|
76
|
+
|
77
|
+
# Publishes a message to the given path and with a given hash context.
|
78
|
+
# @param [String] path the pubsub path
|
79
|
+
# @param [Hash{Symbol => Object}] context the message context
|
80
|
+
# @return [true,false] whether or not all callbacks executed successfully.
|
81
|
+
# @example Publishing a message
|
82
|
+
# hub.publish 'hello:world', :hello => 'world'
|
83
|
+
def publish(path, context = {})
|
84
|
+
path_parts = path.split(":")
|
85
|
+
context = merge_path_context path_parts, context
|
86
|
+
# Actually handle publishing the subscription
|
87
|
+
subscriptions.call(context.merge(:path_parts => path_parts, :full_path => path)) != false
|
88
|
+
end
|
89
|
+
|
90
|
+
def primary?
|
91
|
+
!!@primary
|
92
|
+
end
|
93
|
+
|
94
|
+
def primary=(value)
|
95
|
+
value = !!value
|
96
|
+
if value != @value
|
97
|
+
@value = value
|
98
|
+
ActiveSupport.run_load_hooks :smeg_head_hub, self
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
private
|
103
|
+
|
104
|
+
def merge_path_context(path_parts, context)
|
105
|
+
if context.has_key?(:path_keys)
|
106
|
+
context = context.dup
|
107
|
+
path_keys = Array(context.delete(:path_keys))
|
108
|
+
path_keys.each_with_index do |part, idx|
|
109
|
+
next if part.blank?
|
110
|
+
context[part.to_sym] = path_parts[idx]
|
111
|
+
end
|
112
|
+
end
|
113
|
+
context
|
114
|
+
end
|
115
|
+
|
116
|
+
end
|
117
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'tavern/hub'
|
2
|
+
|
3
|
+
module Tavern
|
4
|
+
# A simple hub you can use to completely disable the pub / sub process
|
5
|
+
# but still record what happens.
|
6
|
+
class MockHub < Hub
|
7
|
+
|
8
|
+
class Publication < Struct.new(:path, :context)
|
9
|
+
end
|
10
|
+
|
11
|
+
attr_reader :subscriptions, :unsubscriptions, :publications
|
12
|
+
|
13
|
+
def initialize
|
14
|
+
super
|
15
|
+
@subscriptions, @unsubscriptions, @publications = [], [], []
|
16
|
+
end
|
17
|
+
|
18
|
+
# Subscribes to a given path string and either a proc callback or
|
19
|
+
# any object responding to #call.
|
20
|
+
# @param [String] path the subscription path
|
21
|
+
# @param [#call] object if present, the callback to invoke
|
22
|
+
# @param [Proc] blk the block to use for the callback (if the object is nil)
|
23
|
+
# @example Subscribing with a block
|
24
|
+
# hub.subscribe 'hello:world' do |ctx|
|
25
|
+
# puts "Context is #{ctx.inspect}"
|
26
|
+
# end
|
27
|
+
# @example Subscribing with an object
|
28
|
+
# hub.subscribe 'hello:world', MyCallableClass
|
29
|
+
def subscribe(path, object = nil, &blk)
|
30
|
+
subscription = Subscription.new(path, (object || blk))
|
31
|
+
subscriptions << subscription
|
32
|
+
subscription
|
33
|
+
end
|
34
|
+
|
35
|
+
# Deletes the given subscription from this pub sub hub.
|
36
|
+
# @param [Subscription] subscription the subscription to delete
|
37
|
+
# @return [Subscription] the deleted subscription
|
38
|
+
def unsubscribe(subscription)
|
39
|
+
return if subscription.blank?
|
40
|
+
subscriptions.delete subscription
|
41
|
+
subscription
|
42
|
+
end
|
43
|
+
|
44
|
+
# Publishes a message to the given path and with a given hash context.
|
45
|
+
# @param [String] path the pubsub path
|
46
|
+
# @param [Hash{Symbol => Object}] context the message context
|
47
|
+
# @example Publishing a message
|
48
|
+
# hub.publish 'hello:world', :hello => 'world'
|
49
|
+
def publish(path, context = {})
|
50
|
+
publications << Publication.new(path, context)
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Tavern
|
2
|
+
# A general subscription in a given hub, including a name and a given callback
|
3
|
+
# block. As part of this, it provides tools to make it easy to handle the subscriptions.
|
4
|
+
class Subscription
|
5
|
+
|
6
|
+
attr_reader :name, :callback
|
7
|
+
|
8
|
+
# Initializes a new subscription with a given name and callback.
|
9
|
+
# @param [String] name the name of this subscription
|
10
|
+
# @param [#call] callback the callback for this subscription
|
11
|
+
def initialize(name, callback)
|
12
|
+
@name = name.to_s
|
13
|
+
@callback = callback
|
14
|
+
@_subscribe_keys = @name.to_s.split(":")
|
15
|
+
end
|
16
|
+
|
17
|
+
# Returns the list of subscription sublevel keys.
|
18
|
+
# @return [Array<String>] the list of sublevel keys
|
19
|
+
def to_subscribe_keys
|
20
|
+
@_subscribe_keys
|
21
|
+
end
|
22
|
+
|
23
|
+
# Invokes the callback, returning whatever it returns.
|
24
|
+
# @param [Hash] context the callback context.
|
25
|
+
def call(context)
|
26
|
+
@callback.call context
|
27
|
+
end
|
28
|
+
|
29
|
+
def to_proc
|
30
|
+
proc { |ctx| call ctx }
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
module Tavern
|
2
|
+
# Tavern::Subscriptions implements a simple tree-like structure for handling subscriptions, facilitating
|
3
|
+
# the efficient depth-first processing of callbacks based on a given path.
|
4
|
+
#
|
5
|
+
# Each level of the tree is composed of some callbacks as well as a set of sublevels, each represented by
|
6
|
+
# part of a subscription namespace. Namely, given a key of "a:b:c", there will be four levels:
|
7
|
+
#
|
8
|
+
# 1. The root level - Currently, nothing subscribes to this level
|
9
|
+
# 2. The "a" level - this is nested under the root level.
|
10
|
+
# 3. The "b" level - this is nested under the "a" level.
|
11
|
+
# 4. The "c" level - this is nested under the "b" level.
|
12
|
+
#
|
13
|
+
# When an event is published to "a:b:c", the top level Subscriptions instance will recursively ``#call`` the
|
14
|
+
# child levels, breaking if at any point a subscription returns false. Ideally, this means that for a normal
|
15
|
+
# event, we will get events (if present) invoked on all four levels, allowing a nice and structured event
|
16
|
+
# dispatch.
|
17
|
+
class Subscriptions
|
18
|
+
|
19
|
+
# Creates a new subscription with an empty list of subscriptions and an empty subkeys mapping.
|
20
|
+
def initialize
|
21
|
+
@subscriptions = []
|
22
|
+
@subkeys = {}
|
23
|
+
end
|
24
|
+
|
25
|
+
# Given a specified context, calls all nested matching subcontexts and then invokes the
|
26
|
+
# callbacks on this level, breaking if it encounters false (like terminators in AS callbacks).
|
27
|
+
#
|
28
|
+
# This means that if your subscription returns false, it will halt further callbacks. Also,
|
29
|
+
# It also means that dispatching events are depth first based on the key. E.g., given a callback
|
30
|
+
# key of "a:b:c", the callbacks for "a:b:c" will be called first followed by those for "a:b" and "a".
|
31
|
+
#
|
32
|
+
# @param [Hash] context the given context for this publish call
|
33
|
+
# @option context [Array<String>] :path_parts an array of the current children keys to dispatch on
|
34
|
+
def call(context = {})
|
35
|
+
path_parts = context[:path_parts].dup
|
36
|
+
# Call the sublevel, breaking when a value returns false
|
37
|
+
if path_parts.any? and (subkey = @subkeys[path_parts.shift])
|
38
|
+
result = subkey.call context.merge(:path_parts => path_parts)
|
39
|
+
return result if result == false
|
40
|
+
end
|
41
|
+
# Iterate over the subscriptions, breaking when one of them returns false
|
42
|
+
@subscriptions.each do |subscription|
|
43
|
+
return false if subscription.call(context) == false
|
44
|
+
end
|
45
|
+
# Otherwise, return nil
|
46
|
+
true
|
47
|
+
end
|
48
|
+
|
49
|
+
# Adds a new subscription to this subscriptions list.
|
50
|
+
# @param [Subscription] subscription the new subscription to add
|
51
|
+
def add(subscription)
|
52
|
+
@subscriptions << subscription
|
53
|
+
end
|
54
|
+
|
55
|
+
# Removes a given subscription from this level.
|
56
|
+
# @param [Subscription] subscription the old subscription to remove
|
57
|
+
def delete(subscription)
|
58
|
+
@subscriptions.delete subscription
|
59
|
+
end
|
60
|
+
|
61
|
+
# Gets the sublevel with the given key, initializing a new
|
62
|
+
# sublevel if it is as of yet unknown.
|
63
|
+
# @param [String] key the key of the given sublevel
|
64
|
+
# @return [Subscriptions] the returned subscription level
|
65
|
+
def sublevel(key)
|
66
|
+
@subkeys[key] ||= Subscriptions.new
|
67
|
+
end
|
68
|
+
|
69
|
+
# Given a list of subkeys, will return the association sublevel.
|
70
|
+
# @param [Array<String>] parts the list of path-parts to reach the desired sublevel.
|
71
|
+
# @return [Subscriptions] the given sublevel subscriptions object.
|
72
|
+
def sublevel_at(*parts)
|
73
|
+
parts.flatten.inject(self) do |level, part|
|
74
|
+
level.sublevel part
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
end
|
79
|
+
end
|
data/lib/tavern.rb
ADDED
data/spec/hub_spec.rb
ADDED
@@ -0,0 +1,115 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Tavern::Hub do
|
4
|
+
|
5
|
+
let(:hub) { Tavern::Hub.new }
|
6
|
+
|
7
|
+
let(:subscriber_klass) do
|
8
|
+
Class.new do
|
9
|
+
|
10
|
+
def published
|
11
|
+
@published ||= []
|
12
|
+
end
|
13
|
+
|
14
|
+
def call(ctx)
|
15
|
+
published << ctx
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
let(:ctx_from_proc) { [] }
|
22
|
+
|
23
|
+
let(:subscriber_proc) do
|
24
|
+
proc { |ctx| ctx_from_proc << ctx }
|
25
|
+
end
|
26
|
+
|
27
|
+
describe 'the primary hub' do
|
28
|
+
|
29
|
+
it 'should let you query if something is the primary hub'
|
30
|
+
|
31
|
+
it 'should run the load hook when the hub is changed'
|
32
|
+
|
33
|
+
it 'should unset it when changing the hub'
|
34
|
+
|
35
|
+
it 'should set it to primary when setting it to the hub value'
|
36
|
+
|
37
|
+
it 'always set the default to be primary'
|
38
|
+
|
39
|
+
end
|
40
|
+
|
41
|
+
describe '#subscribe' do
|
42
|
+
|
43
|
+
it 'should let you subscribe to a top level item'
|
44
|
+
|
45
|
+
it 'should let you subscribe to a nested item'
|
46
|
+
|
47
|
+
it 'should let you pass an object'
|
48
|
+
|
49
|
+
it 'should let you pass a block'
|
50
|
+
|
51
|
+
it 'should return a subscription'
|
52
|
+
|
53
|
+
it 'should automatically subscribe to lower level nested events'
|
54
|
+
|
55
|
+
it 'should raise an error when subscribing with an object that does not provide call'
|
56
|
+
|
57
|
+
end
|
58
|
+
|
59
|
+
describe '#unsubscribe' do
|
60
|
+
|
61
|
+
it 'should remove an object from the subscription pool'
|
62
|
+
|
63
|
+
it 'should return the subscription'
|
64
|
+
|
65
|
+
it 'should do nothing with a blank item'
|
66
|
+
|
67
|
+
end
|
68
|
+
|
69
|
+
describe '#publish' do
|
70
|
+
|
71
|
+
let(:nested_a) { subscriber_klass.new }
|
72
|
+
let(:nested_b) { subscriber_klass.new }
|
73
|
+
let(:nested_c) { subscriber_klass.new }
|
74
|
+
let(:top_level_a) { subscriber_klass.new }
|
75
|
+
let(:top_level_b) { subscriber_klass.new }
|
76
|
+
|
77
|
+
before :each do
|
78
|
+
hub.subscribe 'hello', top_level_a
|
79
|
+
hub.subscribe 'hello:world', nested_a
|
80
|
+
hub.subscribe 'foo', top_level_b
|
81
|
+
hub.subscribe 'foo:bar', nested_b
|
82
|
+
end
|
83
|
+
|
84
|
+
it 'should add the path parts for a top level call' do
|
85
|
+
mock(top_level_a).call(hash_including(:path_parts => %w()))
|
86
|
+
hub.publish 'hello', {}
|
87
|
+
end
|
88
|
+
|
89
|
+
it 'should add the path parts for a nested call' do
|
90
|
+
mock(top_level_a).call(hash_including(:path_parts => %w(world)))
|
91
|
+
mock(nested_a).call(hash_including(:path_parts => %w()))
|
92
|
+
hub.publish 'hello:world', {}
|
93
|
+
end
|
94
|
+
|
95
|
+
it 'should unpack path keys if provided' do
|
96
|
+
mock(top_level_a).call(hash_including(:model_name => 'world'))
|
97
|
+
mock(nested_a).call(hash_including(:model_name => 'world'))
|
98
|
+
hub.publish 'hello:world', :path_keys => [nil, :model_name]
|
99
|
+
end
|
100
|
+
|
101
|
+
it 'should add the full path to the publish' do
|
102
|
+
mock(top_level_a).call(hash_including(:full_path => 'hello:world'))
|
103
|
+
mock(nested_a).call(hash_including(:full_path => 'hello:world'))
|
104
|
+
hub.publish 'hello:world', {}
|
105
|
+
end
|
106
|
+
|
107
|
+
it 'should notify all subscriptions under the path'
|
108
|
+
|
109
|
+
it 'should not notify unmatched subscriptions on a simple case'
|
110
|
+
|
111
|
+
it 'should not notify unmatched subscriptions on a nested case'
|
112
|
+
|
113
|
+
end
|
114
|
+
|
115
|
+
end
|
data/spec/spec_helper.rb
ADDED
data/tavern.gemspec
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path('../lib/tavern/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.authors = ["Darcy Laycock"]
|
6
|
+
gem.email = ["sutto@sutto.net"]
|
7
|
+
gem.description = %q{Tavern implements simple pub / sub systems for Rails applications with a simple, extendable architecture and minimal api surface area.}
|
8
|
+
gem.summary = %q{Simple pubsub for Ruby apps.}
|
9
|
+
gem.homepage = ""
|
10
|
+
|
11
|
+
gem.files = `git ls-files`.split($\)
|
12
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
13
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
14
|
+
gem.name = "tavern"
|
15
|
+
gem.require_paths = ["lib"]
|
16
|
+
gem.version = Tavern::VERSION
|
17
|
+
|
18
|
+
gem.add_dependency 'activesupport', '~> 3.0'
|
19
|
+
gem.add_development_dependency 'rake'
|
20
|
+
gem.add_development_dependency 'rr'
|
21
|
+
gem.add_development_dependency 'rspec', '~> 2.0'
|
22
|
+
end
|
metadata
ADDED
@@ -0,0 +1,127 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: tavern
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Darcy Laycock
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-04-21 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: activesupport
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '3.0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ~>
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '3.0'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: rake
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
type: :development
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: rr
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ! '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: rspec
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ~>
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '2.0'
|
70
|
+
type: :development
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ~>
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '2.0'
|
78
|
+
description: Tavern implements simple pub / sub systems for Rails applications with
|
79
|
+
a simple, extendable architecture and minimal api surface area.
|
80
|
+
email:
|
81
|
+
- sutto@sutto.net
|
82
|
+
executables: []
|
83
|
+
extensions: []
|
84
|
+
extra_rdoc_files: []
|
85
|
+
files:
|
86
|
+
- .gitignore
|
87
|
+
- .rspec
|
88
|
+
- Gemfile
|
89
|
+
- LICENSE
|
90
|
+
- README.md
|
91
|
+
- Rakefile
|
92
|
+
- lib/tavern.rb
|
93
|
+
- lib/tavern/hub.rb
|
94
|
+
- lib/tavern/mock_hub.rb
|
95
|
+
- lib/tavern/subscription.rb
|
96
|
+
- lib/tavern/subscriptions.rb
|
97
|
+
- lib/tavern/version.rb
|
98
|
+
- spec/hub_spec.rb
|
99
|
+
- spec/spec_helper.rb
|
100
|
+
- tavern.gemspec
|
101
|
+
homepage: ''
|
102
|
+
licenses: []
|
103
|
+
post_install_message:
|
104
|
+
rdoc_options: []
|
105
|
+
require_paths:
|
106
|
+
- lib
|
107
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
108
|
+
none: false
|
109
|
+
requirements:
|
110
|
+
- - ! '>='
|
111
|
+
- !ruby/object:Gem::Version
|
112
|
+
version: '0'
|
113
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
114
|
+
none: false
|
115
|
+
requirements:
|
116
|
+
- - ! '>='
|
117
|
+
- !ruby/object:Gem::Version
|
118
|
+
version: '0'
|
119
|
+
requirements: []
|
120
|
+
rubyforge_project:
|
121
|
+
rubygems_version: 1.8.21
|
122
|
+
signing_key:
|
123
|
+
specification_version: 3
|
124
|
+
summary: Simple pubsub for Ruby apps.
|
125
|
+
test_files:
|
126
|
+
- spec/hub_spec.rb
|
127
|
+
- spec/spec_helper.rb
|