├── .gitignore ├── .zapier_ruby.yml.example ├── Gemfile ├── LICENSE.txt ├── README.md ├── Rakefile ├── bin └── zap ├── lib ├── zapier_ruby.rb └── zapier_ruby │ ├── base.rb │ ├── config.rb │ ├── exceptions.rb │ ├── logger_decorator.rb │ ├── version.rb │ └── zapper.rb ├── spec ├── spec_helper.rb └── zapier_ruby_spec.rb └── zapier_ruby.gemspec /.gitignore: -------------------------------------------------------------------------------- 1 | /.bundle/ 2 | /.yardoc 3 | /Gemfile.lock 4 | /_yardoc/ 5 | /coverage/ 6 | /doc/ 7 | /pkg/ 8 | /spec/reports/ 9 | /tmp/ 10 | *.bundle 11 | *.so 12 | *.o 13 | *.a 14 | mkmf.log 15 | -------------------------------------------------------------------------------- /.zapier_ruby.yml.example: -------------------------------------------------------------------------------- 1 | web_hooks: 2 | :test_zap: "xxxxxx" 3 | enable_logging: false 4 | 5 | # for new zaps, grab you account id from your webhook uri 6 | account_id: 1234 7 | 8 | # for older zaps, use this base uri 9 | base_uri: https://zapier.com/hooks/catch/ 10 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | # Specify your gem's dependencies in zapier_ruby.gemspec 4 | gemspec 5 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015 David Peterson 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. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ZapierRuby 2 | 3 | [![Gem Version](https://badge.fury.io/rb/zapier_ruby.svg)](http://badge.fury.io/rb/zapier_ruby) 4 | 5 | Zapier Ruby provides a simple wrapper to post a 'zap' to a Zapier (https://zapier.com) webhook from any Ruby application. You must first have a Zapier account and have created a webhook configured to 'catch hook'. This gem is useful for simple integrations, such as posting to slack when an event happens in your Rails app, or sending an email when your chef deploy has completed. 6 | 7 | ## Installation 8 | 9 | Add this line to your application's Gemfile: 10 | 11 | ```ruby 12 | gem 'zapier_ruby' 13 | ``` 14 | 15 | And then execute: 16 | 17 | $ bundle 18 | 19 | Or install it yourself as: 20 | 21 | $ gem install zapier_ruby 22 | 23 | ## Usage 24 | 25 | ### General Usage 26 | First, configure ZapierRuby. Pass a hash of each of your zap webhooks you would like to integrate, you can also change the uri we post to or disable logging. Next, Instantiate a Zapper for the webhook to hit. Then, use the `zap` method with hash of params and send it to the Zapier web hook. `zap` returns true if it is able to successfully post the zap. 27 | 28 | ```ruby 29 | require 'rubygems' 30 | require 'zapier_ruby' 31 | 32 | ZapierRuby.configure do |c| 33 | c.web_hooks = { example_zap: "webhook_id" } 34 | c.enable_logging = false 35 | # For new web hooks, you must provide this param 36 | c.account_id = "1234" # Get this from the first part of your webhook URI 37 | 38 | # For older webhooks, you should override the base uri to the old uri 39 | c.base_uri = "https://hooks.zapier.com/hooks/catch/" 40 | end 41 | 42 | zapper = ZapierRuby::Zapper.new(:example_zap) 43 | 44 | if zapper.zap({hello: "world"}) 45 | puts "zapped it" 46 | else 47 | puts "it remains unzapped" 48 | end 49 | 50 | ``` 51 | 52 | You can find the value to fill in for "webhook id" and "account id" in the location highlighted below ('xxxxxx' in the green box) when configuring your Zap: 53 | 54 | ![](https://github.com/pete2786/pete2786.github.io/blob/master/images/finding_webhook.png) 55 | 56 | 57 | Each param you send can be used by Zapier, so include all of the information required to complete the task. 58 | 59 | ### Rails Usage 60 | If you are using ZapierRuby with Rails, I'd recommend using creating an initializer (ex. config/intializers/zapier_ruby.rb) and with the following: 61 | 62 | ```ruby 63 | ZapierRuby.configure do |c| 64 | c.web_hooks = {example_zap: "zap_webhook_id"} 65 | end 66 | ``` 67 | 68 | ### Command Line Usage ### 69 | To use this gem from the command line, you can leverage the `bin/zap` Ruby executable. In order to use this gem via command line, you must execute the gem in a folder which has a `.zapier_ruby.yml` file to configure your zaps. An example `.zapier_ruby.yml` file follows: 70 | 71 | ```yaml 72 | web_hooks: 73 | :test_zap: "xxxxxx" 74 | enable_logging: false 75 | 76 | # for new zaps, grab you account id from your webhook uri 77 | account_id: 1234 78 | 79 | # for older zaps, use this base uri 80 | base_uri: https://zapier.com/hooks/catch/ 81 | 82 | ``` 83 | 84 | You must pass `zap [zap_name] [Message]` to the executable. For example: 85 | ``` 86 | bundle exec zap example_zap "Hello, world." 87 | ``` 88 | 89 | Which will post {Message: "Hello, world"} to your web hook. The zap name must be configured in .zapier_ruby.yml the executable will error. 90 | 91 | #### Example Usage 92 | 93 | If you do not have email configured for you application, you could send an email via a Zap to notify a new user that their account has been created. 94 | 95 | ```ruby 96 | class User < ActiveRecord::Base 97 | after_create :welcome_new_user 98 | 99 | def welcome_new_user 100 | ZapierRuby::Zapper.new(:welcome_new_user).zap(user.attributes) 101 | end 102 | end 103 | ``` 104 | 105 | 106 | ## Contributing 107 | 108 | 1. Fork it ( https://github.com/pete2786/zapier_ruby/fork ) 109 | 2. Create your feature branch (`git checkout -b my-new-feature`) 110 | 3. Commit your changes (`git commit -am 'Add some feature'`) 111 | 4. Push to the branch (`git push origin my-new-feature`) 112 | 5. Create a new Pull Request 113 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require "bundler/gem_tasks" 2 | 3 | -------------------------------------------------------------------------------- /bin/zap: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require_relative '../lib/zapier_ruby' 3 | 4 | FILE_NAME = ".zapier_ruby.yml" 5 | 6 | if File.exists?(FILE_NAME) 7 | ZapierRuby.configure_with(File.join(Dir.pwd, FILE_NAME)) 8 | else 9 | raise("Cannot find .zapier_ruby.yml file in pwd") 10 | end 11 | 12 | zap_name = ARGV[0].nil? ? raise("Must supply a zap name") : ARGV[0].to_sym 13 | message = ARGV[1] || "No message supplied." 14 | 15 | puts ZapierRuby::Zapper.new(zap_name).zap({Message: message}) 16 | -------------------------------------------------------------------------------- /lib/zapier_ruby.rb: -------------------------------------------------------------------------------- 1 | require 'net/http' 2 | require 'uri' 3 | require 'json' 4 | require 'yaml' 5 | require 'delegate' 6 | require 'logger' 7 | 8 | require 'zapier_ruby/version' 9 | require 'zapier_ruby/exceptions' 10 | require 'zapier_ruby/logger_decorator' 11 | require 'zapier_ruby/config' 12 | require 'zapier_ruby/base' 13 | require 'zapier_ruby/zapper' 14 | 15 | module ZapierRuby 16 | class << self 17 | attr_accessor :config 18 | end 19 | 20 | def self.configure 21 | self.config ||= ZapierRuby::Config.new 22 | yield(config) if block_given? 23 | end 24 | 25 | def self.configure_with(path_to_yaml_file) 26 | self.config ||= ZapierRuby::Config.new 27 | config.configure_with(path_to_yaml_file) 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /lib/zapier_ruby/base.rb: -------------------------------------------------------------------------------- 1 | module ZapierRuby 2 | class Base 3 | attr_accessor :logger 4 | 5 | private 6 | 7 | def post_zap(params) 8 | uri = URI.parse(zap_url) 9 | logger.debug(uri) 10 | http = Net::HTTP.new(uri.host, uri.port) 11 | http.use_ssl = true 12 | request = Net::HTTP::Post.new(uri.request_uri, zap_headers) 13 | request.body = params.to_json 14 | http.request(request) 15 | rescue StandardError => err 16 | raise ZapierServerError, err 17 | end 18 | 19 | def zap_web_hook_id 20 | @zap_web_hook ||= config.web_hooks[zap_name] 21 | end 22 | 23 | def zap_headers 24 | { 25 | "Accept" => " application/json", 26 | "Content-Type" => "application/json" 27 | } 28 | end 29 | 30 | def zap_url 31 | if config.account_id 32 | "#{config.base_uri}/#{config.account_id}/#{zap_web_hook_id}/" 33 | else 34 | "#{config.base_uri}/#{zap_web_hook_id}/" 35 | end 36 | end 37 | 38 | def config 39 | ZapierRuby.config 40 | end 41 | end 42 | end 43 | -------------------------------------------------------------------------------- /lib/zapier_ruby/config.rb: -------------------------------------------------------------------------------- 1 | module ZapierRuby 2 | class Config 3 | attr_accessor :base_uri, :web_hooks, :enable_logging, :logger, :account_id 4 | 5 | def initialize 6 | self.base_uri = "https://hooks.zapier.com/hooks/catch" 7 | self.web_hooks = { example_webhook: "webhook_id" } 8 | self.enable_logging = true 9 | self.logger = Logger.new(STDOUT) 10 | self.account_id = nil 11 | end 12 | 13 | def configure_with(path_to_yaml_file) 14 | begin 15 | config_yaml = YAML::load(IO.read(path_to_yaml_file)) 16 | reconfigure(config_yaml) 17 | rescue StandardError => e 18 | logger.error "YAML configuration file cannot be loaded. Using defaults. Error :#{e.message}" 19 | return 20 | end 21 | end 22 | 23 | def reconfigure(config = {}) 24 | config.keys.select{|k| respond_to?("#{k}=")}.each{|k| self.send("#{k}=", config[k])} 25 | end 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /lib/zapier_ruby/exceptions.rb: -------------------------------------------------------------------------------- 1 | module ZapierRuby 2 | class ZapierMisConfiguration < StandardError; end 3 | class ZapierServerError < StandardError; end 4 | end 5 | -------------------------------------------------------------------------------- /lib/zapier_ruby/logger_decorator.rb: -------------------------------------------------------------------------------- 1 | module ZapierRuby 2 | class LoggerDecorator < SimpleDelegator 3 | attr_accessor :enable_logging 4 | 5 | def initialize(enable_logging) 6 | self.enable_logging = enable_logging 7 | super(ZapierRuby.config.logger) 8 | end 9 | 10 | def error(message) 11 | super if enable_logging 12 | end 13 | 14 | def info(message) 15 | super if enable_logging 16 | end 17 | 18 | def debug(message) 19 | super if enable_logging 20 | end 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /lib/zapier_ruby/version.rb: -------------------------------------------------------------------------------- 1 | module ZapierRuby 2 | VERSION = "1.0.1" 3 | end 4 | -------------------------------------------------------------------------------- /lib/zapier_ruby/zapper.rb: -------------------------------------------------------------------------------- 1 | module ZapierRuby 2 | class Zapper < Base 3 | attr_accessor :zap_name 4 | 5 | def initialize(zap_name, web_hook_id=nil) 6 | self.zap_name = zap_name 7 | self.logger = LoggerDecorator.new(config.enable_logging) 8 | @zap_web_hook = web_hook_id if web_hook_id 9 | end 10 | 11 | def zap(params={}) 12 | unless zap_web_hook_id 13 | raise ZapierMisConfiguration, "No zap configured for #{zap_name}. Configured webhooks: #{config.web_hooks.to_s}" 14 | end 15 | 16 | logger.debug "Zapping #{zap_name} with params: #{params.to_s}" 17 | post_zap(params) 18 | end 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | require 'bundler/setup' 2 | require 'webmock/rspec' 3 | require 'pry' 4 | require 'zapier_ruby' 5 | 6 | ZapierRuby.configure do |c| 7 | c.web_hooks = { 8 | example_zap: 'webhook_id' 9 | } 10 | c.enable_logging = false 11 | end 12 | 13 | RSpec.configure do |config| 14 | 15 | end 16 | -------------------------------------------------------------------------------- /spec/zapier_ruby_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe 'ZapierRuby' do 4 | let(:body) { { message: 'Hey zapier' } } 5 | let(:result) do 6 | { 7 | "status": "success", 8 | "attempt": "5c304977-e06d-4580-86a3-xxxxxxxxxx", 9 | "id": "096e7041-5510-4f5b-9597-xxxxxxxxxx", 10 | "request_id": "5c304977-e06d-4580-86a3-xxxxxxxxxx" 11 | } 12 | end 13 | 14 | describe 'Zapper' do 15 | let(:zapper) { ZapierRuby::Zapper } 16 | let(:catch_url) { 'https://hooks.zapier.com/hooks/catch/webhook_id/' } 17 | let(:instance) { zapper.new(:example_zap) } 18 | 19 | describe '#zap_url' do 20 | it { expect(instance.send(:zap_url)).to eq catch_url } 21 | 22 | it 'web_hook_id has val' do 23 | url = zapper.new(:nil, 'webhook_id/uuiq').send(:zap_url) 24 | 25 | expect(url).to eq 'https://hooks.zapier.com/hooks/catch/webhook_id/uuiq/' 26 | end 27 | end 28 | 29 | it '.base_uri' do 30 | expect(ZapierRuby.config.base_uri).to eq "https://hooks.zapier.com/hooks/catch" 31 | end 32 | 33 | describe '#zap' do 34 | it 'ZapierMisConfiguration'do 35 | expect { 36 | zapper.new(:typo_zap_name).zap 37 | }.to raise_error(ZapierRuby::ZapierMisConfiguration) 38 | end 39 | 40 | it 'http require' do 41 | stub_request(:post, catch_url).with(body: body).to_return(body: result.to_json) 42 | response = instance.zap(body) 43 | 44 | expect(response.code).to eq "200" 45 | expect(JSON.parse(response.body)['status']).to eq 'success' 46 | end 47 | end 48 | 49 | describe 'with account id' do 50 | it 'has account id in url' do 51 | ZapierRuby.configure { |c| c.account_id = "12345" } 52 | url = zapper.new(:nil, 'uuiq').send(:zap_url) 53 | expect(url).to eq 'https://hooks.zapier.com/hooks/catch/12345/uuiq/' 54 | ZapierRuby.configure { |c| c.account_id = nil } 55 | end 56 | end 57 | 58 | describe 'with base uri configured' do 59 | it 'has account id in url' do 60 | ZapierRuby.configure { |c| c.base_uri = "https://test.com" } 61 | url = zapper.new(:nil, 'uuiq').send(:zap_url) 62 | expect(url).to eq 'https://test.com/uuiq/' 63 | ZapierRuby.configure { |c| c.base_uri = nil } 64 | end 65 | end 66 | end 67 | end 68 | -------------------------------------------------------------------------------- /zapier_ruby.gemspec: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | lib = File.expand_path('../lib', __FILE__) 3 | $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) 4 | require 'zapier_ruby/version' 5 | 6 | Gem::Specification.new do |spec| 7 | spec.name = "zapier_ruby" 8 | spec.version = ZapierRuby::VERSION 9 | spec.authors = ["David Peterson"] 10 | spec.email = ["pete2786@umn.edu"] 11 | spec.summary = %q{Simple gem to integrate Zapier webhooks with a Ruby project.} 12 | spec.description = %q{Simple gem to integrate Zapier webhooks with a Ruby project.} 13 | spec.homepage = "http://pete2786.github.io" 14 | spec.license = "MIT" 15 | 16 | spec.files = `git ls-files -z`.split("\x0") 17 | spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) } 18 | spec.test_files = spec.files.grep(%r{^(test|spec|features)/}) 19 | spec.require_paths = ["lib"] 20 | 21 | spec.add_development_dependency "bundler", "~> 1.7" 22 | spec.add_development_dependency "rake", "~> 10.0" 23 | spec.add_development_dependency "rspec" 24 | spec.add_development_dependency "webmock" 25 | spec.add_development_dependency "pry" 26 | end 27 | --------------------------------------------------------------------------------