omnihooks 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|