omnihooks 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.
- checksums.yaml +7 -0
- data/.gitignore +15 -0
- data/.rspec +2 -0
- data/.ruby-gemsets +1 -0
- data/.ruby-version +1 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +31 -0
- data/Rakefile +2 -0
- data/lib/omnihooks/builder.rb +48 -0
- data/lib/omnihooks/strategies/developer.rb +43 -0
- data/lib/omnihooks/strategy.rb +303 -0
- data/lib/omnihooks/version.rb +3 -0
- data/lib/omnihooks.rb +76 -0
- data/omnihooks.gemspec +27 -0
- data/spec/omnihooks/builder_spec.rb +48 -0
- data/spec/omnihooks/strategy_spec.rb +302 -0
- data/spec/omnihooks_spec.rb +35 -0
- data/spec/spec_helper.rb +115 -0
- metadata +156 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 06339853f7ece3c68f11ba4915da17e824ccb27d
|
4
|
+
data.tar.gz: 2f4d22b89574c3415920c62daed2fb54c432e213
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 52b3d2514746e528b4cf60b2adfa88fe2dc511b1c03eac56c15abfa224b19c88fe822daa024005d65cf020805ab3df569937413319ffb825559948bba285ac22
|
7
|
+
data.tar.gz: c51daa43fbc940543974b40c4d1568c4f62c7be1ab42ef0daa1c1201dfc8cc8e14dabdc3c242d1a1de3c66b53bd533bbbee9c970721768f20d2eae17ac602b62
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.ruby-gemsets
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
omnihooks
|
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
ruby-2.1.6
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2015 Karl Falconer
|
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,31 @@
|
|
1
|
+
# OmniHooks
|
2
|
+
|
3
|
+
TODO: Write a gem description
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
gem 'omnihooks'
|
11
|
+
```
|
12
|
+
|
13
|
+
And then execute:
|
14
|
+
|
15
|
+
$ bundle
|
16
|
+
|
17
|
+
Or install it yourself as:
|
18
|
+
|
19
|
+
$ gem install omnihooks
|
20
|
+
|
21
|
+
## Usage
|
22
|
+
|
23
|
+
TODO: Write usage instructions here
|
24
|
+
|
25
|
+
## Contributing
|
26
|
+
|
27
|
+
1. Fork it ( https://github.com/[my-github-username]/omnihooks/fork )
|
28
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
29
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
30
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
31
|
+
5. Create a new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
require "active_support/inflector"
|
2
|
+
|
3
|
+
module OmniHooks
|
4
|
+
class Builder < ::Rack::Builder
|
5
|
+
|
6
|
+
def initialize(app, &block)
|
7
|
+
@options = nil
|
8
|
+
if rack14?
|
9
|
+
super
|
10
|
+
else
|
11
|
+
@app = app
|
12
|
+
super(&block)
|
13
|
+
@ins << @app
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def rack14?
|
18
|
+
Rack.release.split('.')[1].to_i >= 4
|
19
|
+
end
|
20
|
+
|
21
|
+
def on_failure(&block)
|
22
|
+
OmniHooks.config.on_failure = block
|
23
|
+
end
|
24
|
+
|
25
|
+
def options(options = false)
|
26
|
+
return @options || {} if options == false
|
27
|
+
@options = options
|
28
|
+
end
|
29
|
+
|
30
|
+
def provider(klass, *args, &block)
|
31
|
+
if klass.is_a?(Class)
|
32
|
+
middleware = klass
|
33
|
+
else
|
34
|
+
begin
|
35
|
+
middleware = OmniHooks::Strategies.const_get(klass.to_s.camelize)
|
36
|
+
rescue NameError
|
37
|
+
raise(LoadError.new("Could not find matching strategy for #{klass.inspect}. You may need to install an additional gem (such as omnihooks-#{klass})."))
|
38
|
+
end
|
39
|
+
end
|
40
|
+
args.last.is_a?(Hash) ? args.push(options.merge(args.pop)) : args.push(options)
|
41
|
+
use middleware, *args, &block
|
42
|
+
end
|
43
|
+
|
44
|
+
def call(env)
|
45
|
+
to_app.call(env)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module OmniHooks
|
2
|
+
module Strategies
|
3
|
+
# The Developer strategy is a very simple strategy that can be used as a
|
4
|
+
# placeholder in your application until a different authentication strategy
|
5
|
+
# is swapped in. It has zero security and should *never* be used in a
|
6
|
+
# production setting.
|
7
|
+
#
|
8
|
+
# ## Usage
|
9
|
+
#
|
10
|
+
# To use the Developer strategy, all you need to do is put it in like any
|
11
|
+
# other strategy:
|
12
|
+
#
|
13
|
+
# @example Basic Usage
|
14
|
+
#
|
15
|
+
# use OmniAuth::Builder do
|
16
|
+
# provider :developer
|
17
|
+
# end
|
18
|
+
#
|
19
|
+
# @example Custom Fields
|
20
|
+
#
|
21
|
+
# use OmniAuth::Builder do
|
22
|
+
# provider :developer,
|
23
|
+
# :fields => [:first_name, :last_name],
|
24
|
+
# :uid_field => :last_name
|
25
|
+
# end
|
26
|
+
#
|
27
|
+
# This will create a strategy that, when the user visits `/auth/developer`
|
28
|
+
# they will be presented a form that prompts for (by default) their name
|
29
|
+
# and email address. The auth hash will be populated with these fields and
|
30
|
+
# the `uid` will simply be set to the provided email.
|
31
|
+
class Developer
|
32
|
+
include OmniHooks::Strategy
|
33
|
+
|
34
|
+
event_type do
|
35
|
+
params[:type]
|
36
|
+
end
|
37
|
+
|
38
|
+
event do
|
39
|
+
params
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,303 @@
|
|
1
|
+
require "active_support/notifications"
|
2
|
+
|
3
|
+
module OmniHooks
|
4
|
+
# The Strategy is the base unit of OmniHooks's ability to
|
5
|
+
# wrangle multiple providers. Each strategy provided by
|
6
|
+
# OmniHooks includes this mixin to gain the default functionality
|
7
|
+
# necessary to be compatible with the OmniHooks library.
|
8
|
+
module Strategy # rubocop:disable ModuleLength
|
9
|
+
def self.included(base)
|
10
|
+
OmniHooks.strategies << base
|
11
|
+
|
12
|
+
base.extend ClassMethods
|
13
|
+
base.class_eval do
|
14
|
+
option :backend, ActiveSupport::Notifications
|
15
|
+
option :adapter, OmniHooks::Strategy::NotificationAdapter
|
16
|
+
option :namespace_delimiter, '.'
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
module ClassMethods
|
21
|
+
attr_accessor :namespace
|
22
|
+
# Returns an inherited set of default options set at the class-level
|
23
|
+
# for each strategy.
|
24
|
+
def default_options
|
25
|
+
return @default_options if instance_variable_defined?(:@default_options) && @default_options
|
26
|
+
existing = superclass.respond_to?(:default_options) ? superclass.default_options : {}
|
27
|
+
@default_options = OmniHooks::Strategy::Options.new(existing)
|
28
|
+
end
|
29
|
+
|
30
|
+
# Directly declare a default option for your class. This is a useful from
|
31
|
+
# a documentation perspective as it provides a simple line-by-line analysis
|
32
|
+
# of the kinds of options your strategy provides by default.
|
33
|
+
#
|
34
|
+
# @param name [Symbol] The key of the default option in your configuration hash.
|
35
|
+
# @param value [Object] The value your object defaults to. Nil if not provided.
|
36
|
+
#
|
37
|
+
# @example
|
38
|
+
#
|
39
|
+
# class MyStrategy
|
40
|
+
# include OmniAuth::Strategy
|
41
|
+
#
|
42
|
+
# option :foo, 'bar'
|
43
|
+
# option
|
44
|
+
# end
|
45
|
+
def option(name, value = nil)
|
46
|
+
default_options[name] = value
|
47
|
+
end
|
48
|
+
|
49
|
+
# Sets (and retrieves) option key names for initializer arguments to be
|
50
|
+
# recorded as. This takes care of 90% of the use cases for overriding
|
51
|
+
# the initializer in OmniAuth Strategies.
|
52
|
+
def args(args = nil)
|
53
|
+
if args
|
54
|
+
@args = Array(args)
|
55
|
+
return
|
56
|
+
end
|
57
|
+
existing = superclass.respond_to?(:args) ? superclass.args : []
|
58
|
+
(instance_variable_defined?(:@args) && @args) || existing
|
59
|
+
end
|
60
|
+
# This allows for more declarative subclassing of strategies by allowing
|
61
|
+
# default options to be set using a simple configure call.
|
62
|
+
#
|
63
|
+
# @param options [Hash] If supplied, these will be the default options (deep-merged into the superclass's default options).
|
64
|
+
# @yield [Options] The options Mash that allows you to set your defaults as you'd like.
|
65
|
+
#
|
66
|
+
# @example Using a yield to configure the default options.
|
67
|
+
#
|
68
|
+
# class MyStrategy
|
69
|
+
# include OmniHooks::Strategy
|
70
|
+
#
|
71
|
+
# configure_options do |c|
|
72
|
+
# c.foo = 'bar'
|
73
|
+
# end
|
74
|
+
# end
|
75
|
+
#
|
76
|
+
# @example Using a hash to configure the default options.
|
77
|
+
#
|
78
|
+
# class MyStrategy
|
79
|
+
# include OmniHooks::Strategy
|
80
|
+
# configure_options foo: 'bar'
|
81
|
+
# end
|
82
|
+
def configure_options(options = nil)
|
83
|
+
if block_given?
|
84
|
+
yield default_options
|
85
|
+
else
|
86
|
+
default_options.deep_merge!(options)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def configure(&block)
|
91
|
+
raise ArgumentError, "must provide a block" unless block_given?
|
92
|
+
block.arity.zero? ? instance_eval(&block) : yield(self)
|
93
|
+
end
|
94
|
+
alias :setup :configure
|
95
|
+
|
96
|
+
def subscribe(name, callable = Proc.new)
|
97
|
+
backend.subscribe(namespace.to_regexp(name), adapter.call(callable))
|
98
|
+
end
|
99
|
+
|
100
|
+
def all(callable = Proc.new)
|
101
|
+
backend.subscribe(nil, callable)
|
102
|
+
end
|
103
|
+
|
104
|
+
def listening?(name)
|
105
|
+
namespaced_name = namespace.call(name)
|
106
|
+
backend.notifier.listening?(namespaced_name)
|
107
|
+
end
|
108
|
+
#protected
|
109
|
+
%w(event_type event).each do |fetcher|
|
110
|
+
class_eval <<-RUBY
|
111
|
+
def #{fetcher}(&block)
|
112
|
+
@#{fetcher}_proc = nil unless defined?(@#{fetcher}_proc)
|
113
|
+
|
114
|
+
return @#{fetcher}_proc unless block_given?
|
115
|
+
@#{fetcher}_proc = block
|
116
|
+
end
|
117
|
+
def #{fetcher}_stack(context)
|
118
|
+
compile_stack(self.ancestors, :#{fetcher}, context)
|
119
|
+
end
|
120
|
+
RUBY
|
121
|
+
end
|
122
|
+
|
123
|
+
def instrument(event_type, event_object)
|
124
|
+
backend.instrument(namespace.call(event_type), event_object)
|
125
|
+
end
|
126
|
+
|
127
|
+
private
|
128
|
+
|
129
|
+
def compile_stack(ancestors, method, context)
|
130
|
+
stack = ancestors.inject([]) do |a, ancestor|
|
131
|
+
a << context.instance_eval(&ancestor.send(method)) if ancestor.respond_to?(method) && ancestor.send(method)
|
132
|
+
a
|
133
|
+
end
|
134
|
+
stack.reverse!
|
135
|
+
end
|
136
|
+
|
137
|
+
def adapter
|
138
|
+
default_options.adapter
|
139
|
+
end
|
140
|
+
|
141
|
+
def backend
|
142
|
+
default_options.backend
|
143
|
+
end
|
144
|
+
|
145
|
+
def namespace
|
146
|
+
@namespace ||= OmniHooks::Strategy::Namespace.new(default_options.name, default_options.namespace_delimiter)
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
class Namespace < Struct.new(:prefix, :delimiter)
|
151
|
+
def call(name = nil)
|
152
|
+
"#{prefix}#{delimiter}#{name}"
|
153
|
+
end
|
154
|
+
|
155
|
+
def to_regexp(name = nil)
|
156
|
+
%r{^#{Regexp.escape(call(name))}}
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
class NotificationAdapter < Struct.new(:subscriber)
|
161
|
+
def self.call(callable)
|
162
|
+
new(callable)
|
163
|
+
end
|
164
|
+
|
165
|
+
def call(*args)
|
166
|
+
payload = args.last
|
167
|
+
subscriber.call(payload)
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
class Options < Hashie::Mash; end
|
172
|
+
|
173
|
+
attr_reader :options
|
174
|
+
|
175
|
+
# Initializes the strategy by passing in the Rack endpoint,
|
176
|
+
# the unique URL segment name for this strategy, and any
|
177
|
+
# additional arguments. An `options` hash is automatically
|
178
|
+
# created from the last argument if it is a hash.
|
179
|
+
#
|
180
|
+
# @param app [Rack application] The application on which this middleware is applied.
|
181
|
+
#
|
182
|
+
# @overload new(app, options = {})
|
183
|
+
# If nothing but a hash is supplied, initialized with the supplied options
|
184
|
+
# overriding the strategy's default options via a deep merge.
|
185
|
+
# @overload new(app, *args, options = {})
|
186
|
+
# If the strategy has supplied custom arguments that it accepts, they may
|
187
|
+
# will be passed through and set to the appropriate values.
|
188
|
+
#
|
189
|
+
# @yield [Class, Options] Yields Parent class and options to block for further configuration.
|
190
|
+
def initialize(app, *args, &block) # rubocop:disable UnusedMethodArgument
|
191
|
+
@app = app
|
192
|
+
@env = nil
|
193
|
+
@options = self.class.default_options.dup
|
194
|
+
|
195
|
+
options.deep_merge!(args.pop) if args.last.is_a?(Hash)
|
196
|
+
|
197
|
+
self.class.args.each do |arg|
|
198
|
+
break if args.empty?
|
199
|
+
options[arg] = args.shift
|
200
|
+
end
|
201
|
+
|
202
|
+
# Make sure that all of the args have been dealt with, otherwise error out.
|
203
|
+
fail(ArgumentError.new("Received wrong number of arguments. #{args.inspect}")) unless args.empty?
|
204
|
+
|
205
|
+
yield self.class, options if block_given?
|
206
|
+
end
|
207
|
+
|
208
|
+
def inspect
|
209
|
+
"#<#{self.class}>"
|
210
|
+
end
|
211
|
+
|
212
|
+
# Duplicates this instance and runs #call! on it.
|
213
|
+
# @param [Hash] The Rack environment.
|
214
|
+
def call(env)
|
215
|
+
dup.call!(env)
|
216
|
+
end
|
217
|
+
# The logic for dispatching any additional actions that need
|
218
|
+
# to be taken. For instance, calling the request phase if
|
219
|
+
# the request path is recognized.
|
220
|
+
#
|
221
|
+
# @param env [Hash] The Rack environment.
|
222
|
+
def call!(env) # rubocop:disable CyclomaticComplexity, PerceivedComplexity
|
223
|
+
@env = env
|
224
|
+
|
225
|
+
return instrument if on_request_path? && OmniHooks.config.allowed_request_methods.include?(request.request_method.downcase.to_sym)
|
226
|
+
|
227
|
+
@app.call(env)
|
228
|
+
end
|
229
|
+
|
230
|
+
def request
|
231
|
+
@request ||= Rack::Request.new(@env)
|
232
|
+
end
|
233
|
+
|
234
|
+
protected
|
235
|
+
attr_reader :app, :env
|
236
|
+
|
237
|
+
# Direct access to the OmniAuth logger, automatically prefixed
|
238
|
+
# with this strategy's name.
|
239
|
+
#
|
240
|
+
# @example
|
241
|
+
# log :warn, "This is a warning."
|
242
|
+
def log(level, message)
|
243
|
+
OmniHooks.logger.send(level, "(#{name}) #{message}")
|
244
|
+
end
|
245
|
+
|
246
|
+
private
|
247
|
+
|
248
|
+
CURRENT_PATH_REGEX = %r{/$}
|
249
|
+
EMPTY_STRING = ''.freeze
|
250
|
+
|
251
|
+
def instrument
|
252
|
+
# instance needs to lookup and from the paylook the event type
|
253
|
+
begin
|
254
|
+
evt = get_event
|
255
|
+
evt_type = get_event_type
|
256
|
+
self.class.instrument(evt_type, evt) if evt
|
257
|
+
rescue => e
|
258
|
+
log(:error, e.message)
|
259
|
+
[500, {}, [nil]]
|
260
|
+
else
|
261
|
+
# Send a 200 response back to
|
262
|
+
[200, {}, [nil]]
|
263
|
+
end
|
264
|
+
end
|
265
|
+
|
266
|
+
def path_prefix
|
267
|
+
options[:path_prefix] || OmniHooks.config.path_prefix
|
268
|
+
end
|
269
|
+
|
270
|
+
def name
|
271
|
+
options.name
|
272
|
+
end
|
273
|
+
|
274
|
+
def request_path
|
275
|
+
@request_path ||= options[:request_path].is_a?(String) ? options[:request_path] : "#{path_prefix}/#{name}"
|
276
|
+
end
|
277
|
+
|
278
|
+
def on_request_path?
|
279
|
+
if options.request_path.respond_to?(:call)
|
280
|
+
options.request_path.call(env)
|
281
|
+
else
|
282
|
+
on_path?(request_path)
|
283
|
+
end
|
284
|
+
end
|
285
|
+
|
286
|
+
def on_path?(path)
|
287
|
+
current_path.casecmp(path) == 0
|
288
|
+
end
|
289
|
+
|
290
|
+
def current_path
|
291
|
+
@current_path ||= request.path_info.downcase.sub(CURRENT_PATH_REGEX, EMPTY_STRING)
|
292
|
+
end
|
293
|
+
|
294
|
+
def get_event_type
|
295
|
+
self.class.event_type_stack(self).last
|
296
|
+
end
|
297
|
+
|
298
|
+
def get_event
|
299
|
+
self.class.event_stack(self).last
|
300
|
+
end
|
301
|
+
|
302
|
+
end
|
303
|
+
end
|
data/lib/omnihooks.rb
ADDED
@@ -0,0 +1,76 @@
|
|
1
|
+
require 'rack'
|
2
|
+
require 'singleton'
|
3
|
+
require 'logger'
|
4
|
+
require 'hashie'
|
5
|
+
require 'omnihooks/builder'
|
6
|
+
require 'omnihooks/strategy'
|
7
|
+
|
8
|
+
module OmniHooks
|
9
|
+
|
10
|
+
module Strategies
|
11
|
+
end
|
12
|
+
|
13
|
+
#
|
14
|
+
# Collection of current strategies
|
15
|
+
#
|
16
|
+
# @return [Array<Object>] strategy collection
|
17
|
+
def self.strategies
|
18
|
+
@strategies ||= []
|
19
|
+
end
|
20
|
+
|
21
|
+
class Configuration
|
22
|
+
include Singleton
|
23
|
+
|
24
|
+
attr_accessor :logger, :path_prefix, :allowed_request_methods
|
25
|
+
|
26
|
+
def self.default_logger
|
27
|
+
logger = Logger.new(STDOUT)
|
28
|
+
logger.progname = 'omnihooks'
|
29
|
+
logger
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.defaults
|
33
|
+
@defaults ||= {
|
34
|
+
logger: default_logger,
|
35
|
+
path_prefix: '/hooks',
|
36
|
+
allowed_request_methods: [:post]
|
37
|
+
}
|
38
|
+
end
|
39
|
+
|
40
|
+
def initialize
|
41
|
+
self.class.defaults.each_pair { |k, v| send("#{k}=", v) }
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
|
46
|
+
def self.config
|
47
|
+
Configuration.instance
|
48
|
+
end
|
49
|
+
|
50
|
+
def self.configure
|
51
|
+
yield config
|
52
|
+
end
|
53
|
+
|
54
|
+
def self.logger
|
55
|
+
config.logger
|
56
|
+
end
|
57
|
+
|
58
|
+
module Utils
|
59
|
+
module_function
|
60
|
+
|
61
|
+
def deep_merge(hash, other_hash)
|
62
|
+
target = hash.dup
|
63
|
+
|
64
|
+
other_hash.keys.each do |key|
|
65
|
+
if other_hash[key].is_a?(::Hash) && hash[key].is_a?(::Hash)
|
66
|
+
target[key] = deep_merge(target[key], other_hash[key])
|
67
|
+
next
|
68
|
+
end
|
69
|
+
|
70
|
+
target[key] = other_hash[key]
|
71
|
+
end
|
72
|
+
|
73
|
+
target
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
data/omnihooks.gemspec
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'omnihooks/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "omnihooks"
|
8
|
+
spec.version = OmniHooks::VERSION
|
9
|
+
spec.authors = ["Karl Falconer"]
|
10
|
+
spec.email = ["karl.falconer@falconerdevelopment.com"]
|
11
|
+
spec.summary = 'A generalized framework for multiple-provider webhooks subscriptions.'
|
12
|
+
spec.homepage = ""
|
13
|
+
spec.license = "MIT"
|
14
|
+
|
15
|
+
spec.files = `git ls-files -z`.split("\x0")
|
16
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
17
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
18
|
+
spec.require_paths = ["lib"]
|
19
|
+
|
20
|
+
spec.add_dependency "activesupport", ">= 3.1"
|
21
|
+
spec.add_dependency 'rack', '~> 1.0'
|
22
|
+
spec.add_dependency 'hashie', ['>= 1.2', '< 4']
|
23
|
+
|
24
|
+
spec.add_development_dependency "bundler", "~> 1.7"
|
25
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
26
|
+
spec.add_development_dependency 'rspec', '~> 3.3.0'
|
27
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe OmniHooks::Builder do
|
4
|
+
describe '#provider' do
|
5
|
+
it 'translates a symbol to a constant' do
|
6
|
+
expect(OmniHooks::Strategies).to receive(:const_get).with('MyStrategy').and_return(Class.new)
|
7
|
+
OmniHooks::Builder.new(nil) do
|
8
|
+
provider :my_strategy
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'accepts a class' do
|
13
|
+
class ExampleClass; end
|
14
|
+
expect do
|
15
|
+
OmniHooks::Builder.new(nil) do
|
16
|
+
provider ::ExampleClass
|
17
|
+
end
|
18
|
+
end.not_to raise_error
|
19
|
+
end
|
20
|
+
|
21
|
+
it "raises a helpful LoadError message if it can't find the class" do
|
22
|
+
expect do
|
23
|
+
OmniHooks::Builder.new(nil) do
|
24
|
+
provider :lorax
|
25
|
+
end
|
26
|
+
end.to raise_error(LoadError, 'Could not find matching strategy for :lorax. You may need to install an additional gem (such as omnihooks-lorax).')
|
27
|
+
end
|
28
|
+
end
|
29
|
+
describe '#options' do
|
30
|
+
it 'merges provided options in' do
|
31
|
+
k = Class.new
|
32
|
+
b = OmniHooks::Builder.new(nil)
|
33
|
+
expect(b).to receive(:use).with(k, :foo => 'bar', :baz => 'tik')
|
34
|
+
|
35
|
+
b.options :foo => 'bar'
|
36
|
+
b.provider k, :baz => 'tik'
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'adds an argument if no options are provided' do
|
40
|
+
k = Class.new
|
41
|
+
b = OmniHooks::Builder.new(nil)
|
42
|
+
expect(b).to receive(:use).with(k, :foo => 'bar')
|
43
|
+
|
44
|
+
b.options :foo => 'bar'
|
45
|
+
b.provider k
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,302 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
def make_env(path = '/hooks/test', props = {})
|
4
|
+
{
|
5
|
+
'REQUEST_METHOD' => 'POST',
|
6
|
+
'PATH_INFO' => path,
|
7
|
+
'rack.session' => {},
|
8
|
+
'rack.input' => StringIO.new('test=true'),
|
9
|
+
}.merge(props)
|
10
|
+
end
|
11
|
+
|
12
|
+
|
13
|
+
RSpec.describe OmniHooks::Strategy do
|
14
|
+
let(:app) do
|
15
|
+
lambda { |_env| [404, {}, ['Awesome']] }
|
16
|
+
end
|
17
|
+
|
18
|
+
let(:fresh_strategy) do
|
19
|
+
c = Class.new
|
20
|
+
c.send(:include, OmniHooks::Strategy)
|
21
|
+
end
|
22
|
+
|
23
|
+
describe '.default_options' do
|
24
|
+
it 'is inherited from a parent class' do
|
25
|
+
superklass = Class.new
|
26
|
+
superklass.send :include, OmniHooks::Strategy
|
27
|
+
superklass.configure_options do |c|
|
28
|
+
c.foo = 'bar'
|
29
|
+
end
|
30
|
+
|
31
|
+
klass = Class.new(superklass)
|
32
|
+
expect(klass.default_options.foo).to eq('bar')
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
describe '.configure_options' do
|
37
|
+
subject do
|
38
|
+
c = Class.new
|
39
|
+
c.send(:include, OmniHooks::Strategy)
|
40
|
+
end
|
41
|
+
|
42
|
+
context 'when block is passed' do
|
43
|
+
it 'allows for default options setting' do
|
44
|
+
subject.configure_options do |c|
|
45
|
+
c.wakka = 'doo'
|
46
|
+
end
|
47
|
+
expect(subject.new(nil).options['wakka']).to eq('doo')
|
48
|
+
end
|
49
|
+
|
50
|
+
it "works when block doesn't evaluate to true" do
|
51
|
+
environment_variable = nil
|
52
|
+
subject.configure_options do |c|
|
53
|
+
c.abc = '123'
|
54
|
+
c.hgi = environment_variable
|
55
|
+
end
|
56
|
+
expect(subject.new(nil).options['abc']).to eq('123')
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
it 'takes a hash and deep merge it' do
|
61
|
+
subject.configure_options :abc => {:def => 123}
|
62
|
+
subject.configure_options :abc => {:hgi => 456}
|
63
|
+
expect(subject.new(nil).options['abc']).to eq('def' => 123, 'hgi' => 456)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
describe '.option' do
|
68
|
+
subject do
|
69
|
+
c = Class.new
|
70
|
+
c.send(:include, OmniHooks::Strategy)
|
71
|
+
end
|
72
|
+
it 'sets a default value' do
|
73
|
+
subject.option :abc, 123
|
74
|
+
expect(subject.new(nil).options.abc).to eq(123)
|
75
|
+
end
|
76
|
+
|
77
|
+
it 'sets the default value to nil if none is provided' do
|
78
|
+
subject.option :abc
|
79
|
+
expect(subject.new(nil).options.abc).to be_nil
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
describe '.args' do
|
84
|
+
subject do
|
85
|
+
c = Class.new
|
86
|
+
c.send(:include, OmniHooks::Strategy)
|
87
|
+
end
|
88
|
+
|
89
|
+
it 'sets args to the specified argument if there is one' do
|
90
|
+
subject.args [:abc, :def]
|
91
|
+
expect(subject.args).to eq([:abc, :def])
|
92
|
+
end
|
93
|
+
|
94
|
+
it 'is inheritable' do
|
95
|
+
subject.args [:abc, :def]
|
96
|
+
c = Class.new(subject)
|
97
|
+
expect(c.args).to eq([:abc, :def])
|
98
|
+
end
|
99
|
+
|
100
|
+
it 'accepts corresponding options as default arg values' do
|
101
|
+
subject.args [:a, :b]
|
102
|
+
subject.option :a, '1'
|
103
|
+
subject.option :b, '2'
|
104
|
+
|
105
|
+
expect(subject.new(nil).options.a).to eq '1'
|
106
|
+
expect(subject.new(nil).options.b).to eq '2'
|
107
|
+
expect(subject.new(nil, '3', '4').options.b).to eq '4'
|
108
|
+
expect(subject.new(nil, nil, '4').options.a).to eq nil
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
describe '.instrument' do
|
113
|
+
subject do
|
114
|
+
c = Class.new
|
115
|
+
c.send(:include, OmniHooks::Strategy)
|
116
|
+
c.option :name, 'class'
|
117
|
+
c
|
118
|
+
end
|
119
|
+
|
120
|
+
it 'should forward event publication to backend' do
|
121
|
+
expect(ActiveSupport::Notifications).to receive(:instrument).with('class.foo', 'bar')
|
122
|
+
subject.instrument('foo', 'bar')
|
123
|
+
end
|
124
|
+
|
125
|
+
end
|
126
|
+
|
127
|
+
context 'fetcher procs' do
|
128
|
+
subject { fresh_strategy }
|
129
|
+
%w(event event_type).each do |fetcher|
|
130
|
+
describe ".#{fetcher}" do
|
131
|
+
it 'sets and retrieve a proc' do
|
132
|
+
proc = lambda { 'Hello' }
|
133
|
+
subject.send(fetcher, &proc)
|
134
|
+
expect(subject.send(fetcher)).to eq(proc)
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
context 'fetcher stacks' do
|
141
|
+
subject { fresh_strategy }
|
142
|
+
%w(event event_type).each do |fetcher|
|
143
|
+
describe ".#{fetcher}_stack" do
|
144
|
+
it 'is an array of called ancestral procs' do
|
145
|
+
fetchy = proc { 'Hello' }
|
146
|
+
subject.send(fetcher, &fetchy)
|
147
|
+
expect(subject.send("#{fetcher}_stack", subject.new(app))).to eq(['Hello'])
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
describe '#initialize' do
|
154
|
+
context 'options extraction' do
|
155
|
+
it 'is the last argument if the last argument is a Hash' do
|
156
|
+
expect(ExampleStrategy.new(app, :abc => 123).options[:abc]).to eq(123)
|
157
|
+
end
|
158
|
+
|
159
|
+
it 'is the default options if any are provided' do
|
160
|
+
allow(ExampleStrategy).to receive(:default_options).and_return(OmniHooks::Strategy::Options.new(:abc => 123))
|
161
|
+
expect(ExampleStrategy.new(app).options.abc).to eq(123)
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
context 'custom args' do
|
166
|
+
subject do
|
167
|
+
c = Class.new
|
168
|
+
c.send(:include, OmniHooks::Strategy)
|
169
|
+
end
|
170
|
+
|
171
|
+
it 'sets options based on the arguments if they are supplied' do
|
172
|
+
subject.args [:abc, :def]
|
173
|
+
s = subject.new app, 123, 456
|
174
|
+
expect(s.options[:abc]).to eq(123)
|
175
|
+
expect(s.options[:def]).to eq(456)
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
describe '#call' do
|
181
|
+
before(:all) do
|
182
|
+
@options = nil
|
183
|
+
end
|
184
|
+
|
185
|
+
let(:strategy) { ExampleStrategy.new(app, @options || {}) }
|
186
|
+
|
187
|
+
it 'duplicates and calls' do
|
188
|
+
klass = Class.new
|
189
|
+
klass.send :include, OmniHooks::Strategy
|
190
|
+
instance = klass.new(app)
|
191
|
+
expect(instance).to receive(:dup).and_return(instance)
|
192
|
+
instance.call('rack.session' => {})
|
193
|
+
end
|
194
|
+
|
195
|
+
context 'without a subscriber' do
|
196
|
+
it 'should return a sucess response' do
|
197
|
+
klass = Class.new
|
198
|
+
klass.send :include, OmniHooks::Strategy
|
199
|
+
klass.option :name, 'class'
|
200
|
+
klass.event { 'Foo' }
|
201
|
+
klass.event_type { 'bar' }
|
202
|
+
instance = klass.new(app)
|
203
|
+
|
204
|
+
expect(ActiveSupport::Notifications).to receive(:instrument).with('class.bar', 'Foo')
|
205
|
+
|
206
|
+
expect(instance.call(make_env('/hooks/class'))).to eq([200, {}, [nil]])
|
207
|
+
end
|
208
|
+
|
209
|
+
context 'with exception in event callback' do
|
210
|
+
let(:klass) { Class.new }
|
211
|
+
before(:each) do
|
212
|
+
|
213
|
+
klass.send :include, OmniHooks::Strategy
|
214
|
+
klass.option :name, 'class'
|
215
|
+
klass.event { raise 'Foo' }
|
216
|
+
end
|
217
|
+
|
218
|
+
it 'should not raise an error' do
|
219
|
+
instance = klass.new(app)
|
220
|
+
expect { instance.call(make_env('/hooks/class')) }.not_to raise_error
|
221
|
+
end
|
222
|
+
|
223
|
+
it 'should return a non 200 response' do
|
224
|
+
instance = klass.new(app)
|
225
|
+
expect(instance.call(make_env('/hooks/class'))).to eq([500, {}, [nil]])
|
226
|
+
end
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
context 'with an explicit subscriber' do
|
231
|
+
let(:subscriber) { Proc.new { nil } }
|
232
|
+
before(:each) do
|
233
|
+
|
234
|
+
ExampleStrategy.event { 'Foo' }
|
235
|
+
ExampleStrategy.event_type { request.params['type'] }
|
236
|
+
ExampleStrategy.configure do |events|
|
237
|
+
events.subscribe('foo.bar', subscriber)
|
238
|
+
end
|
239
|
+
end
|
240
|
+
|
241
|
+
context 'with matched event type' do
|
242
|
+
it 'should return a success response' do
|
243
|
+
expect(subscriber).to receive(:call).with('Foo')
|
244
|
+
|
245
|
+
expect(strategy.call(make_env('/hooks/test', {'rack.input' => StringIO.new('type=foo.bar&payload=test')}))).to eq([200, {}, [nil]])
|
246
|
+
end
|
247
|
+
end
|
248
|
+
|
249
|
+
context 'with unmatched event' do
|
250
|
+
it 'should return a success response' do
|
251
|
+
expect(subscriber).not_to receive(:call)
|
252
|
+
|
253
|
+
expect(strategy.call(make_env('/hooks/test', {'rack.input' => StringIO.new('type=foo.sam&payload=test')}))).to eq([200, {}, [nil]])
|
254
|
+
end
|
255
|
+
end
|
256
|
+
|
257
|
+
context 'with an exception in the subscriber' do
|
258
|
+
before(:each) do
|
259
|
+
expect(subscriber).to receive(:call).and_raise(RuntimeError)
|
260
|
+
end
|
261
|
+
|
262
|
+
it 'should return an error response' do
|
263
|
+
expect(strategy.call(make_env('/hooks/test', {'rack.input' => StringIO.new('type=foo.bar&payload=test')}))).to eq([500, {}, [nil]])
|
264
|
+
end
|
265
|
+
end
|
266
|
+
|
267
|
+
after(:each) do
|
268
|
+
# reset the handlers
|
269
|
+
ExampleStrategy.event
|
270
|
+
ExampleStrategy.event_type
|
271
|
+
end
|
272
|
+
end
|
273
|
+
|
274
|
+
context 'request method restriction' do
|
275
|
+
before do
|
276
|
+
OmniHooks.config.allowed_request_methods = [:put]
|
277
|
+
end
|
278
|
+
|
279
|
+
it 'does not allow a request method of the wrong type' do
|
280
|
+
expect { strategy.call(make_env) }.not_to raise_error
|
281
|
+
end
|
282
|
+
|
283
|
+
it 'forwards request method of the wrong type to application' do
|
284
|
+
expect(strategy.call(make_env)).to eq([404, {}, ['Awesome']])
|
285
|
+
end
|
286
|
+
|
287
|
+
it 'allows a request method of the correct type' do
|
288
|
+
expect(strategy.call(make_env('/hooks/test', 'REQUEST_METHOD' => 'PUT'))).to eq([200, {}, [nil]])
|
289
|
+
end
|
290
|
+
|
291
|
+
after do
|
292
|
+
OmniHooks.config.allowed_request_methods = [:post]
|
293
|
+
end
|
294
|
+
end
|
295
|
+
end
|
296
|
+
|
297
|
+
describe '#inspect' do
|
298
|
+
it 'returns the class name' do
|
299
|
+
expect(ExampleStrategy.new(app).inspect).to eq('#<ExampleStrategy>')
|
300
|
+
end
|
301
|
+
end
|
302
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe OmniHooks do
|
4
|
+
describe '.strategies' do
|
5
|
+
it 'increases when a new strategy is made' do
|
6
|
+
expect {
|
7
|
+
class ExampleStrategy
|
8
|
+
include OmniHooks::Strategy
|
9
|
+
end
|
10
|
+
}.to change(OmniHooks.strategies, :size).by(1)
|
11
|
+
expect(OmniHooks.strategies.last).to eq(ExampleStrategy)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
context 'configuration' do
|
16
|
+
describe '.defaults' do
|
17
|
+
it 'is a hash of default configuration' do
|
18
|
+
expect(OmniHooks::Configuration.defaults).to be_kind_of(Hash)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'is callable from .configure' do
|
23
|
+
OmniHooks.configure do |c|
|
24
|
+
expect(c).to be_kind_of(OmniHooks::Configuration)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
describe '.logger' do
|
30
|
+
it 'calls through to the configured logger' do
|
31
|
+
allow(OmniHooks).to receive(:config).and_return(double(:logger => 'foo'))
|
32
|
+
expect(OmniHooks.logger).to eq('foo')
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,115 @@
|
|
1
|
+
require 'rspec'
|
2
|
+
require 'omnihooks'
|
3
|
+
|
4
|
+
|
5
|
+
# This file was generated by the `rspec --init` command. Conventionally, all
|
6
|
+
# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
|
7
|
+
# The generated `.rspec` file contains `--require spec_helper` which will cause
|
8
|
+
# this file to always be loaded, without a need to explicitly require it in any
|
9
|
+
# files.
|
10
|
+
#
|
11
|
+
# Given that it is always loaded, you are encouraged to keep this file as
|
12
|
+
# light-weight as possible. Requiring heavyweight dependencies from this file
|
13
|
+
# will add to the boot time of your test suite on EVERY test run, even for an
|
14
|
+
# individual file that may not need all of that loaded. Instead, consider making
|
15
|
+
# a separate helper file that requires the additional dependencies and performs
|
16
|
+
# the additional setup, and require it from the spec files that actually need
|
17
|
+
# it.
|
18
|
+
#
|
19
|
+
# The `.rspec` file also contains a few flags that are not defaults but that
|
20
|
+
# users commonly want.
|
21
|
+
#
|
22
|
+
# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
|
23
|
+
RSpec.configure do |config|
|
24
|
+
# rspec-expectations config goes here. You can use an alternate
|
25
|
+
# assertion/expectation library such as wrong or the stdlib/minitest
|
26
|
+
# assertions if you prefer.
|
27
|
+
config.expect_with :rspec do |expectations|
|
28
|
+
# This option will default to `true` in RSpec 4. It makes the `description`
|
29
|
+
# and `failure_message` of custom matchers include text for helper methods
|
30
|
+
# defined using `chain`, e.g.:
|
31
|
+
# be_bigger_than(2).and_smaller_than(4).description
|
32
|
+
# # => "be bigger than 2 and smaller than 4"
|
33
|
+
# ...rather than:
|
34
|
+
# # => "be bigger than 2"
|
35
|
+
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
|
36
|
+
end
|
37
|
+
|
38
|
+
# rspec-mocks config goes here. You can use an alternate test double
|
39
|
+
# library (such as bogus or mocha) by changing the `mock_with` option here.
|
40
|
+
config.mock_with :rspec do |mocks|
|
41
|
+
# Prevents you from mocking or stubbing a method that does not exist on
|
42
|
+
# a real object. This is generally recommended, and will default to
|
43
|
+
# `true` in RSpec 4.
|
44
|
+
mocks.verify_partial_doubles = true
|
45
|
+
end
|
46
|
+
|
47
|
+
# The settings below are suggested to provide a good initial experience
|
48
|
+
# with RSpec, but feel free to customize to your heart's content.
|
49
|
+
|
50
|
+
# These two settings work together to allow you to limit a spec run
|
51
|
+
# to individual examples or groups you care about by tagging them with
|
52
|
+
# `:focus` metadata. When nothing is tagged with `:focus`, all examples
|
53
|
+
# get run.
|
54
|
+
config.filter_run :focus
|
55
|
+
config.run_all_when_everything_filtered = true
|
56
|
+
|
57
|
+
# Allows RSpec to persist some state between runs in order to support
|
58
|
+
# the `--only-failures` and `--next-failure` CLI options. We recommend
|
59
|
+
# you configure your source control system to ignore this file.
|
60
|
+
config.example_status_persistence_file_path = "spec/examples.txt"
|
61
|
+
|
62
|
+
# Limits the available syntax to the non-monkey patched syntax that is
|
63
|
+
# recommended. For more details, see:
|
64
|
+
# - http://myronmars.to/n/dev-blog/2012/06/rspecs-new-expectation-syntax
|
65
|
+
# - http://www.teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/
|
66
|
+
# - http://myronmars.to/n/dev-blog/2014/05/notable-changes-in-rspec-3#new__config_option_to_disable_rspeccore_monkey_patching
|
67
|
+
config.disable_monkey_patching!
|
68
|
+
|
69
|
+
# This setting enables warnings. It's recommended, but in some cases may
|
70
|
+
# be too noisy due to issues in dependencies.
|
71
|
+
config.warnings = true
|
72
|
+
|
73
|
+
# Many RSpec users commonly either run the entire suite or an individual
|
74
|
+
# file, and it's useful to allow more verbose output when running an
|
75
|
+
# individual spec file.
|
76
|
+
if config.files_to_run.one?
|
77
|
+
# Use the documentation formatter for detailed output,
|
78
|
+
# unless a formatter has already been configured
|
79
|
+
# (e.g. via a command-line flag).
|
80
|
+
config.default_formatter = 'doc'
|
81
|
+
end
|
82
|
+
|
83
|
+
# Print the 10 slowest examples and example groups at the
|
84
|
+
# end of the spec run, to help surface which specs are running
|
85
|
+
# particularly slow.
|
86
|
+
config.profile_examples = 10
|
87
|
+
|
88
|
+
# Run specs in random order to surface order dependencies. If you find an
|
89
|
+
# order dependency and want to debug it, you can fix the order by providing
|
90
|
+
# the seed, which is printed after each run.
|
91
|
+
# --seed 1234
|
92
|
+
config.order = :random
|
93
|
+
|
94
|
+
# Seed global randomization in this process using the `--seed` CLI option.
|
95
|
+
# Setting this allows you to use `--seed` to deterministically reproduce
|
96
|
+
# test failures related to randomization by passing the same `--seed` value
|
97
|
+
# as the one that triggered the failure.
|
98
|
+
Kernel.srand config.seed
|
99
|
+
|
100
|
+
end
|
101
|
+
|
102
|
+
class ExampleStrategy
|
103
|
+
include OmniHooks::Strategy
|
104
|
+
attr_reader :last_env
|
105
|
+
option :name, 'test'
|
106
|
+
|
107
|
+
def call(env)
|
108
|
+
self.call!(env)
|
109
|
+
end
|
110
|
+
|
111
|
+
def initialize(*args, &block)
|
112
|
+
super
|
113
|
+
@fail = nil
|
114
|
+
end
|
115
|
+
end
|
metadata
ADDED
@@ -0,0 +1,156 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: omnihooks
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Karl Falconer
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-09-02 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: activesupport
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '3.1'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '3.1'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rack
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: hashie
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '1.2'
|
48
|
+
- - "<"
|
49
|
+
- !ruby/object:Gem::Version
|
50
|
+
version: '4'
|
51
|
+
type: :runtime
|
52
|
+
prerelease: false
|
53
|
+
version_requirements: !ruby/object:Gem::Requirement
|
54
|
+
requirements:
|
55
|
+
- - ">="
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
version: '1.2'
|
58
|
+
- - "<"
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: '4'
|
61
|
+
- !ruby/object:Gem::Dependency
|
62
|
+
name: bundler
|
63
|
+
requirement: !ruby/object:Gem::Requirement
|
64
|
+
requirements:
|
65
|
+
- - "~>"
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: '1.7'
|
68
|
+
type: :development
|
69
|
+
prerelease: false
|
70
|
+
version_requirements: !ruby/object:Gem::Requirement
|
71
|
+
requirements:
|
72
|
+
- - "~>"
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: '1.7'
|
75
|
+
- !ruby/object:Gem::Dependency
|
76
|
+
name: rake
|
77
|
+
requirement: !ruby/object:Gem::Requirement
|
78
|
+
requirements:
|
79
|
+
- - "~>"
|
80
|
+
- !ruby/object:Gem::Version
|
81
|
+
version: '10.0'
|
82
|
+
type: :development
|
83
|
+
prerelease: false
|
84
|
+
version_requirements: !ruby/object:Gem::Requirement
|
85
|
+
requirements:
|
86
|
+
- - "~>"
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
version: '10.0'
|
89
|
+
- !ruby/object:Gem::Dependency
|
90
|
+
name: rspec
|
91
|
+
requirement: !ruby/object:Gem::Requirement
|
92
|
+
requirements:
|
93
|
+
- - "~>"
|
94
|
+
- !ruby/object:Gem::Version
|
95
|
+
version: 3.3.0
|
96
|
+
type: :development
|
97
|
+
prerelease: false
|
98
|
+
version_requirements: !ruby/object:Gem::Requirement
|
99
|
+
requirements:
|
100
|
+
- - "~>"
|
101
|
+
- !ruby/object:Gem::Version
|
102
|
+
version: 3.3.0
|
103
|
+
description:
|
104
|
+
email:
|
105
|
+
- karl.falconer@falconerdevelopment.com
|
106
|
+
executables: []
|
107
|
+
extensions: []
|
108
|
+
extra_rdoc_files: []
|
109
|
+
files:
|
110
|
+
- ".gitignore"
|
111
|
+
- ".rspec"
|
112
|
+
- ".ruby-gemsets"
|
113
|
+
- ".ruby-version"
|
114
|
+
- Gemfile
|
115
|
+
- LICENSE.txt
|
116
|
+
- README.md
|
117
|
+
- Rakefile
|
118
|
+
- lib/omnihooks.rb
|
119
|
+
- lib/omnihooks/builder.rb
|
120
|
+
- lib/omnihooks/strategies/developer.rb
|
121
|
+
- lib/omnihooks/strategy.rb
|
122
|
+
- lib/omnihooks/version.rb
|
123
|
+
- omnihooks.gemspec
|
124
|
+
- spec/omnihooks/builder_spec.rb
|
125
|
+
- spec/omnihooks/strategy_spec.rb
|
126
|
+
- spec/omnihooks_spec.rb
|
127
|
+
- spec/spec_helper.rb
|
128
|
+
homepage: ''
|
129
|
+
licenses:
|
130
|
+
- MIT
|
131
|
+
metadata: {}
|
132
|
+
post_install_message:
|
133
|
+
rdoc_options: []
|
134
|
+
require_paths:
|
135
|
+
- lib
|
136
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
137
|
+
requirements:
|
138
|
+
- - ">="
|
139
|
+
- !ruby/object:Gem::Version
|
140
|
+
version: '0'
|
141
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
142
|
+
requirements:
|
143
|
+
- - ">="
|
144
|
+
- !ruby/object:Gem::Version
|
145
|
+
version: '0'
|
146
|
+
requirements: []
|
147
|
+
rubyforge_project:
|
148
|
+
rubygems_version: 2.4.8
|
149
|
+
signing_key:
|
150
|
+
specification_version: 4
|
151
|
+
summary: A generalized framework for multiple-provider webhooks subscriptions.
|
152
|
+
test_files:
|
153
|
+
- spec/omnihooks/builder_spec.rb
|
154
|
+
- spec/omnihooks/strategy_spec.rb
|
155
|
+
- spec/omnihooks_spec.rb
|
156
|
+
- spec/spec_helper.rb
|