webhookr 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (68) hide show
  1. data/.gitignore +14 -0
  2. data/Gemfile +18 -0
  3. data/Guardfile +31 -0
  4. data/MIT-LICENSE +22 -0
  5. data/README.md +159 -0
  6. data/Rakefile +46 -0
  7. data/app/controllers/webhookr/events_controller.rb +34 -0
  8. data/config/routes.rb +4 -0
  9. data/lib/generators/webhookr/add_route_generator.rb +12 -0
  10. data/lib/generators/webhookr/init_generator.rb +30 -0
  11. data/lib/tasks/webhookr_tasks.rake +16 -0
  12. data/lib/webhookr/adapter_response.rb +3 -0
  13. data/lib/webhookr/engine.rb +16 -0
  14. data/lib/webhookr/invalid_payload_error.rb +3 -0
  15. data/lib/webhookr/invalid_security_token_error.rb +3 -0
  16. data/lib/webhookr/ostruct_utils.rb +28 -0
  17. data/lib/webhookr/service.rb +51 -0
  18. data/lib/webhookr/services/adapter/base.rb +20 -0
  19. data/lib/webhookr/services/adapter.rb +7 -0
  20. data/lib/webhookr/services.rb +7 -0
  21. data/lib/webhookr/version.rb +3 -0
  22. data/lib/webhookr.rb +24 -0
  23. data/script/rails +8 -0
  24. data/test/dummy/README.rdoc +261 -0
  25. data/test/dummy/Rakefile +7 -0
  26. data/test/dummy/app/assets/javascripts/application.js +15 -0
  27. data/test/dummy/app/assets/stylesheets/application.css +13 -0
  28. data/test/dummy/app/controllers/application_controller.rb +3 -0
  29. data/test/dummy/app/helpers/application_helper.rb +2 -0
  30. data/test/dummy/app/mailers/.gitkeep +0 -0
  31. data/test/dummy/app/models/.gitkeep +0 -0
  32. data/test/dummy/app/views/layouts/application.html.erb +14 -0
  33. data/test/dummy/config/application.rb +65 -0
  34. data/test/dummy/config/boot.rb +10 -0
  35. data/test/dummy/config/environment.rb +5 -0
  36. data/test/dummy/config/environments/development.rb +31 -0
  37. data/test/dummy/config/environments/production.rb +64 -0
  38. data/test/dummy/config/environments/test.rb +35 -0
  39. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  40. data/test/dummy/config/initializers/inflections.rb +15 -0
  41. data/test/dummy/config/initializers/mime_types.rb +5 -0
  42. data/test/dummy/config/initializers/secret_token.rb +7 -0
  43. data/test/dummy/config/initializers/session_store.rb +8 -0
  44. data/test/dummy/config/initializers/webhookr.rb +2 -0
  45. data/test/dummy/config/initializers/wrap_parameters.rb +10 -0
  46. data/test/dummy/config/locales/en.yml +5 -0
  47. data/test/dummy/config/routes.rb +3 -0
  48. data/test/dummy/config.ru +4 -0
  49. data/test/dummy/lib/assets/.gitkeep +0 -0
  50. data/test/dummy/log/.gitkeep +0 -0
  51. data/test/dummy/public/404.html +26 -0
  52. data/test/dummy/public/422.html +26 -0
  53. data/test/dummy/public/500.html +25 -0
  54. data/test/dummy/public/favicon.ico +0 -0
  55. data/test/dummy/script/rails +6 -0
  56. data/test/functional/webhookr/events_controller_test.rb +76 -0
  57. data/test/functional/webhookr/events_routes_test.rb +33 -0
  58. data/test/functional/webhookr/service_test.rb +91 -0
  59. data/test/integration/webhookr/add_route_generator_test.rb +15 -0
  60. data/test/integration/webhookr/init_generator_test.rb +27 -0
  61. data/test/stubs/service_under_test_stubs.rb +73 -0
  62. data/test/test_helper.rb +18 -0
  63. data/test/unit/webhookr/Services/ServiceUnderTest/adapter_test.rb +39 -0
  64. data/test/unit/webhookr/adapter_response_test.rb +20 -0
  65. data/test/unit/webhookr/ostruct_utils_test.rb +32 -0
  66. data/test/webhookr_test.rb +11 -0
  67. data/webhookr.gemspec +22 -0
  68. metadata +171 -0
data/.gitignore ADDED
@@ -0,0 +1,14 @@
1
+ README.html
2
+ *.orig
3
+ .*.swp
4
+ .*.swo
5
+ *.tmp
6
+ *.patch
7
+ *.kpf
8
+ *~
9
+ .DS_Store
10
+ Thumbs.db
11
+ Gemfile.lock
12
+ Gemfile.local
13
+ Guardfile.local
14
+ *.log
data/Gemfile ADDED
@@ -0,0 +1,18 @@
1
+ source "http://rubygems.org"
2
+
3
+ gemspec
4
+
5
+ gem "rake", "~> 10.0"
6
+ gem "minitest"
7
+ gem "minitest-reporters"
8
+ gem "em-websocket"
9
+ gem "guard"
10
+ gem "guard-minitest"
11
+ gem "guard-markdown"
12
+ gem "guard-livereload"
13
+ gem "simplecov", :require => false
14
+
15
+ if File.exists?('Gemfile.local')
16
+ instance_eval File.read('Gemfile.local')
17
+ end
18
+
data/Guardfile ADDED
@@ -0,0 +1,31 @@
1
+
2
+ guard 'minitest', :test_folders => 'test', :test_file_patterns => '*_test.rb' do
3
+ watch(%r|^test/(.+)_test\.rb|)
4
+ watch(%r|^test/stubs/(.+)\.rb$|) { "test" }
5
+
6
+ # Rails
7
+ watch(%r{^app/models/(.+)\.rb$}) { |m|
8
+ "test/unit/#{m[1]}_test.rb"
9
+ }
10
+
11
+ watch(%r{^app/controllers/(.+)\.rb$}) { |m|
12
+ "test/functional/#{m[1]}_test.rb"
13
+ }
14
+
15
+ watch('config/routes.rb') {
16
+ ["test/functional", "test/integration"]
17
+ }
18
+ end
19
+
20
+ guard 'livereload' do
21
+ watch('README.md')
22
+ end
23
+
24
+ guard 'markdown', :convert_on_start => true do
25
+ watch ('README.md') { "./README.md|./README.html" }
26
+ end
27
+
28
+ if File.exists?('Guardfile.local')
29
+ instance_eval File.read('Guardfile.local')
30
+ end
31
+
data/MIT-LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 2167961 Ontario Inc., Zoocasa <code@zoocasa.com>
2
+
3
+ Permission is hereby granted, free of charge, to any person
4
+ obtaining a copy of this software and associated documentation
5
+ files (the "Software"), to deal in the Software without
6
+ restriction, including without limitation the rights to use,
7
+ copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ copies of the Software, and to permit persons to whom the
9
+ Software is furnished to do so, subject to the following
10
+ conditions:
11
+
12
+ The above copyright notice and this permission notice shall be
13
+ included in all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
17
+ OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
19
+ HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20
+ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22
+ OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,159 @@
1
+ # webhookr: Rails Webhooks Made Easy
2
+ [![Code Climate](https://codeclimate.com/badge.png)](https://codeclimate.com/github/zoocasa/webhookr)
3
+
4
+ ## <a name="introduction"></a>Introduction
5
+
6
+ ### What is webhookr?
7
+
8
+ webhookr is a Rails Engine that easily and securely adds third-party
9
+ webhooks to your Rails app. It supports third-party sites with modular
10
+ plugins.
11
+
12
+ ### What are 'webhooks'?
13
+
14
+ From 30,000 feet, webhooks allow disparate cloud services to coordinate and
15
+ asynchronously notify each other of events of interest. For example, a shopping app might
16
+ want to process a [customer credit card transaction](https://stripe.com/docs/webhooks),
17
+ and be notified of success or failure.
18
+
19
+ From 10,000 feet, webhooks allow external third parties the ability to
20
+ execute code within your application, using a well defined message exchange
21
+ protocol.
22
+
23
+ From 1,000 feet and below, webhooks are application routes and controller
24
+ endpoints, third-party API discovery, security decisions, and code that
25
+ executes within your application.
26
+
27
+
28
+ ### Why use webhookr?
29
+
30
+ Webhooks are http callbacks from another service that are useful for
31
+ pushing information to your Rails application. The question is: How do I
32
+ easily and securely add them to my Rails application?
33
+
34
+ webhookr provides a single, secure route for all your webhooks, and uses
35
+ the observer pattern to integrate the webhook with your business logic.
36
+ Using webhookr you can be up and running with a third-party webhook in less
37
+ than an hour.
38
+
39
+ ### How does webhookr work?
40
+
41
+ Webhookr provides a standard approach for third-party webhooks via plugins. The
42
+ author of a plugin implements code to standardize any webhook into an action and
43
+ data packet. Your task is to write glue code that is called whenever an action is
44
+ received, and to use the resulting data packet for your application logic. Here is
45
+ a subset of the MailChimp handler that might be written to deal with the 'unsubscribe'
46
+ action sent from MailChimp:
47
+
48
+ ```ruby
49
+ class MailChimpHooks
50
+ def on_unsubscribe(payload)
51
+ User.unsubscribe_newletter(payload.data.email)
52
+ end
53
+ end
54
+ ```
55
+
56
+ ## <a name="usage"></a>Usage & Setup
57
+
58
+ webhookr works with Rails 3.1 onwards. It generally requires a plugin to be
59
+ useful, such as the [MailChimp plugin](https://github.com/zoocasa/webhookr-mailchimp).
60
+
61
+ ## Setup
62
+
63
+ Add webhookr to your Gemfile with:
64
+
65
+ ```ruby
66
+ gem 'webhookr'
67
+ ```
68
+
69
+ Run the bundle command to install it and then run the generator to
70
+ add the engine route to your config/routes.rb:
71
+
72
+ ```console
73
+ rails g webhookr:add_route
74
+ ```
75
+
76
+ or, add the routing information manually to config/routes.rb
77
+
78
+ ```ruby
79
+ mount Webhookr::Engine => "/webhookr", :as => "webhookr"
80
+ ```
81
+
82
+ The initialization file for webhookr is optional. To see what
83
+ options are available, you can run the generator and review the
84
+ sample config file:
85
+
86
+ ```console
87
+ rails g webhookr:init *initializer_name*
88
+ ```
89
+
90
+ Once you have added third-party plugins, you can use the provided
91
+ rake task to output your applications urls for each service:
92
+
93
+ ```console
94
+ rake webhookr:services
95
+ ```
96
+
97
+ ## <a name="security"></a>Webhookr Security
98
+
99
+ ### General security issues with webhooks
100
+
101
+ A webhook is by design, a http post to your application that results in code execution.
102
+ Having a well defined approach to adding such functionality can help ensure you don't open security issues
103
+ within your application.
104
+
105
+ Many webhook providers do not provide built-in security for their
106
+ webhooks - they usually send the post over http, and they often have no authentication
107
+ or verification mechanism. If your webhook third-party service responds to hooks, it is
108
+ possible for an evil person to guess your webhook URL, and attempt to wreak havoc with your system.
109
+
110
+ ### How using webhookr can increase security
111
+ webhookr provides a unique url for each service, via a security token that is used in the webhook path, to help make it hard to guess your webhook url.
112
+ For example, if you were using MailChimp, your webhook url might look like: '/webhookr/mailchimp/cdd2a24cfac821' and if you were using Mandrill, your
113
+ webhook might look like: '/webhookr/mandrill/de69557a4d95e7'. This will help prevent someone guessing your
114
+ webhook service url. If you are using https, the URL will be encrypted, keeping your security token a secret.
115
+
116
+ You can also enable http basic auth, if the third party service supports it. Note that enabling http basic auth
117
+ affects all your webhooks, so be sure it is supported by all your third-party services.
118
+
119
+ If you are sending sensitive data via webhooks, it is recommended you use HTTPS.
120
+
121
+ ### <a name="supported_services"></a>Supported Services
122
+
123
+ * MailChimp - webhookr-mail_chimp
124
+ * Mandrill - webhookr-mandrill
125
+ * Github - coming soon
126
+ * Stripe - coming soon
127
+
128
+ ## <a name="works_with"></a>Works with:
129
+
130
+ webhookr works with Rails 3.1 and 3.2, and has been tested on the following Ruby
131
+ implementations:
132
+
133
+ * JRuby 1.7.1
134
+ * MRI 1.8.7
135
+ * MRI 1.9.2
136
+ * MRI 1.9.3
137
+ * Rubinius 1.2.4
138
+ * Ruby EE 1.8.7
139
+
140
+ Pending:
141
+
142
+ * MRI 2.0
143
+
144
+ ### TODO
145
+ * Implement get/post strategies and responses so the controller can return variable text to the service.
146
+ This allows support for advanced Webhook responses for services that require it.
147
+ * Enhance testing of Rake tasks
148
+ * Clean up the stubs with FactoryGirl
149
+ * Test with MRI 2.0
150
+
151
+ ### License
152
+
153
+ webhookr is released under the [MIT license](http://www.opensource.org/licenses/MIT).
154
+
155
+ ## Author
156
+
157
+ * [Gerry Power](https://github.com/gerrypower)
158
+
159
+ ## <a name="Version History"></a>Version History
data/Rakefile ADDED
@@ -0,0 +1,46 @@
1
+
2
+ # -*- ruby -*-
3
+
4
+ require 'rubygems'
5
+ require 'rubygems/package_task'
6
+ require 'rake/testtask'
7
+ require 'rdoc/task'
8
+ require 'bundler/gem_tasks'
9
+
10
+ $:.push File.expand_path(File.dirname(__FILE__), 'lib')
11
+
12
+ version = Webhookr::VERSION
13
+
14
+ desc 'Test Webhookr'
15
+ Rake::TestTask.new(:test) do |t|
16
+ t.test_files = FileList['test/**/*_test.rb']
17
+ t.verbose = !!ENV['VERBOSE_TESTS']
18
+ t.warning = !!ENV['WARNINGS']
19
+ end
20
+
21
+ desc 'Build docs'
22
+ Rake::RDocTask.new do |t|
23
+ t.main = 'README.md'
24
+ t.title = "Webhookr #{version}"
25
+ t.rdoc_dir = 'doc'
26
+ t.rdoc_files.include('README.md', 'MIT-LICENSE', 'lib/**/*.rb')
27
+ end
28
+
29
+ namespace :webhookr do
30
+ namespace:test do
31
+ desc 'Install gems in all Rubies'
32
+ task :install do
33
+ sh %{rbenv each -v bundle install}
34
+ end
35
+
36
+ desc 'Test with all Rubies'
37
+ task :test_versions do
38
+ sh %{rbenv each -v bundle exec rake test}
39
+ end
40
+
41
+ desc 'Install and test all'
42
+ task :all => [:install, :test_versions]
43
+ end
44
+ end
45
+
46
+ task :default => :test
@@ -0,0 +1,34 @@
1
+ module Webhookr
2
+ class EventsController < ActionController::Base
3
+ http_basic_authenticate_with(
4
+ :name => Webhookr.config.basic_auth.username,
5
+ :password => Webhookr.config.basic_auth.password
6
+ ) if Webhookr.config.basic_auth.username && Webhookr.config.basic_auth.password
7
+
8
+ before_filter :create_service
9
+
10
+ def show
11
+ render :nothing => true
12
+ end
13
+
14
+ def create
15
+ @service.process!
16
+ render :nothing => true
17
+ end
18
+
19
+ private
20
+
21
+ def create_service
22
+ begin
23
+ @service = Webhookr::Service.new(
24
+ params[:service_id], :payload => request.body.read, :security_token => params[:security_token]
25
+ )
26
+ rescue NameError => e
27
+ raise ActionController::RoutingError.new("No service '#{params[:service_id]}' is available.")
28
+ rescue Webhookr::InvalidSecurityTokenError => e
29
+ raise ActionController::InvalidAuthenticityToken.new("Invalid or missing security token for service '#{params[:service_id]}'.")
30
+ end
31
+ end
32
+
33
+ end
34
+ end
data/config/routes.rb ADDED
@@ -0,0 +1,4 @@
1
+ Webhookr::Engine.routes.draw do
2
+ get "/events/:service_id(/:security_token)" => "events#show", :as => "events"
3
+ post "/events/:service_id(/:security_token)" => "events#create", :as => "events"
4
+ end
@@ -0,0 +1,12 @@
1
+ module Webhookr
2
+ module Generators
3
+ class AddRouteGenerator < Rails::Generators::Base
4
+
5
+ desc 'This generator adds \'mount Webhookr::Engine => "/webhookr", :as => "webhookr"\' to your routes'
6
+ def add_route
7
+ route 'mount Webhookr::Engine => "/webhookr", :as => "webhookr"'
8
+ end
9
+
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,30 @@
1
+ module Webhookr
2
+ module Generators
3
+ class InitGenerator < Rails::Generators::NamedBase
4
+
5
+ desc "This generator creates an initializer file 'config/initializers/NAME.rb'"
6
+ def init
7
+ initializer("#{file_name}.rb") do
8
+ file_contents
9
+ end
10
+ end
11
+
12
+ def file_contents
13
+ <<-eos
14
+ # Webhookr Initializer
15
+
16
+ ## Turn on http basic authentication for all plugins
17
+ # Webhookr.config.basic_auth.username = "admin"
18
+ # Webhookr.config.basic_auth.password = "password"
19
+
20
+ ## Plugin Initializers go here ##
21
+ eos
22
+ end
23
+
24
+ def generate_security_token
25
+ rand(10000000000000000).floor.to_s(36)
26
+ end
27
+
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,16 @@
1
+ namespace :webhookr do
2
+ desc "List the configured services and paths"
3
+ task :services => :environment do
4
+
5
+ puts "No webhookr services configured - add and configure webhookr plugins." and next if Webhookr.adapters.empty?
6
+
7
+ include Webhookr::Engine.routes.url_helpers
8
+
9
+ Webhookr.adapters.each do |key, adapter|
10
+ puts "\n\n#{key}:"
11
+ %w{ GET POST}.each do |x|
12
+ puts " #{x}\t#{events_path(key, :security_token => Webhookr.config[key].try(:security_token))}\n"
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,3 @@
1
+ module Webhookr
2
+ class AdapterResponse < Struct.new(:service_name, :event_type, :payload); end
3
+ end
@@ -0,0 +1,16 @@
1
+ require 'active_support/core_ext/kernel/singleton_class'
2
+
3
+ module Webhookr
4
+ class Engine < ::Rails::Engine
5
+ isolate_namespace Webhookr
6
+
7
+ # Enable basic auth for all services when config.basic_auth.username
8
+ # and config.basic_auth.password are set.
9
+ self.config.webhookr = ActiveSupport::OrderedOptions.new
10
+ self.config.webhookr.basic_auth = ActiveSupport::OrderedOptions.new
11
+
12
+ initializer "webhookr.config" do |app|
13
+ end
14
+ end
15
+ end
16
+
@@ -0,0 +1,3 @@
1
+ module Webhookr
2
+ class InvalidPayloadError < RuntimeError; end
3
+ end
@@ -0,0 +1,3 @@
1
+ module Webhookr
2
+ class InvalidSecurityTokenError < RuntimeError; end
3
+ end
@@ -0,0 +1,28 @@
1
+ module Webhookr
2
+ # Adapted from http://www.rebeccamiller-webster.com/2012/06/recursively-convert-a-ruby-hash-to-openstruct/
3
+ module OstructUtils
4
+
5
+ def self.to_ostruct(obj)
6
+ case
7
+ when obj.kind_of?(Hash)
8
+ return hash_to_ostruct(obj)
9
+ when obj.kind_of?(Array)
10
+ return array_to_ostruct(obj)
11
+ else
12
+ return obj
13
+ end
14
+ end
15
+
16
+ def self.hash_to_ostruct(hash)
17
+ hash.each do |key, val|
18
+ hash[key] = to_ostruct(val)
19
+ end
20
+ OpenStruct.new(hash)
21
+ end
22
+
23
+ def self.array_to_ostruct(array)
24
+ array.map { |r| to_ostruct(r) }
25
+ end
26
+
27
+ end
28
+ end
@@ -0,0 +1,51 @@
1
+ module Webhookr
2
+ class Service
3
+ attr_reader :service_name
4
+
5
+ def initialize(service_name, options = {})
6
+ @service_name = (service_name || "").downcase
7
+ @raw_payload = options[:payload]
8
+ available?
9
+ validate_security_token(options[:security_token]) if configured_security_token
10
+ end
11
+
12
+ def process!
13
+ Array.wrap(service_adapter.send(:process, @raw_payload)).each do |payload|
14
+ callback(callback_class, payload)
15
+ end
16
+ end
17
+
18
+ private
19
+
20
+ def callback(object, payload)
21
+ method = method_for(payload)
22
+ object.send(method, payload) if object.respond_to?(method)
23
+ end
24
+
25
+ def method_for(payload)
26
+ "on_" + payload.event_type
27
+ end
28
+
29
+ def callback_class
30
+ callback = Webhookr.config[service_name].try(:callback)
31
+ raise "No callback is configured for the service '#{service_name}'." if callback.nil?
32
+ @call_back_class || callback.new
33
+ end
34
+
35
+ def configured_security_token
36
+ Webhookr.config[service_name].try(:security_token)
37
+ end
38
+
39
+ def validate_security_token(token)
40
+ raise Webhookr::InvalidSecurityTokenError if token.nil? || token != configured_security_token
41
+ end
42
+
43
+ def service_adapter
44
+ raise NameError.new(%{Bad service name "#{service_name}"}) unless Webhookr.adapters[service_name]
45
+ @service_adapter ||= Webhookr.adapters[service_name]
46
+ end
47
+
48
+ alias_method :available?, :service_adapter
49
+
50
+ end
51
+ end
@@ -0,0 +1,20 @@
1
+
2
+ module Webhookr::Services::Adapter::Base
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ self.initialize! if self.respond_to?(:initialize!)
7
+ Webhookr.adapters[self::SERVICE_NAME] = self
8
+ end
9
+
10
+ module ClassMethods
11
+ def config
12
+ if Webhookr.config[self::SERVICE_NAME]
13
+ Webhookr.config[self::SERVICE_NAME]
14
+ else
15
+ Webhookr.config[self::SERVICE_NAME] = ActiveSupport::OrderedOptions.new
16
+ end
17
+ end
18
+ end
19
+ end
20
+
@@ -0,0 +1,7 @@
1
+
2
+ module Webhookr::Services::Adapter
3
+ extend ActiveSupport::Autoload
4
+
5
+ autoload :Base
6
+ end
7
+
@@ -0,0 +1,7 @@
1
+
2
+ module Webhookr::Services
3
+ extend ActiveSupport::Autoload
4
+
5
+ autoload :Adapter
6
+ end
7
+
@@ -0,0 +1,3 @@
1
+ module Webhookr
2
+ VERSION = "0.0.2"
3
+ end
data/lib/webhookr.rb ADDED
@@ -0,0 +1,24 @@
1
+ require "webhookr/engine"
2
+
3
+ module Webhookr
4
+ extend ActiveSupport::Autoload
5
+
6
+ autoload :InvalidPayloadError
7
+ autoload :AdapterResponse
8
+ autoload :Service
9
+ autoload :VERSION
10
+
11
+ class << self
12
+ def adapters
13
+ @adapters ||= HashWithIndifferentAccess.new
14
+ end
15
+
16
+ def config
17
+ @config ||= defined?(Rails) ? Rails.application.config.webhookr :
18
+ ActiveSupport::OrderedOptions.new
19
+ end
20
+ end
21
+ end
22
+
23
+ require "webhookr/services"
24
+
data/script/rails ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env ruby
2
+ # This command will automatically be run when you run "rails" with Rails 3 gems installed from the root of your application.
3
+
4
+ ENGINE_ROOT = File.expand_path('../..', __FILE__)
5
+ ENGINE_PATH = File.expand_path('../../lib/webhookr/engine', __FILE__)
6
+
7
+ require 'rails/all'
8
+ require 'rails/engine/commands'