# Pushpop [![Build Status](https://travis-ci.org/keenlabs/pushpop.svg)](https://travis-ci.org/keenlabs/pushpop) ### Automatic delivery of regular reports and alerts
Pingpong Daily Response Time Report     There were 5402 Pageviews today!
## Overview Pushpop is a simple but powerful Ruby app that sends notifications based on events captured with Keen IO. #### Ways to use Pushpop **Regular reports** + Send a sales report to your inbox every day at noon + Send analytics reports to your customers every week **Alerts** + Send an SMS if the performance of your signup funnel dramatically changes + Send an email when your site has been busier than usual in the last hour #### An example Pushpop job Here's a simple Pushpop job that uses [Twilio](https://twilio.com/) to send an SMS containing the number of daily pageviews each night at midnight: ``` ruby require 'pushpop' job do every 24.hours, at: '00:00' keen do event_collection 'pageviews' analysis_type 'count' timeframe 'last_24_hours' end twilio do |response| to '+18005555555' body "There were #{response} pageviews today!" end end ``` Pushpop syntax is short and sweet, but because anything Ruby can be used it's also quite powerful. ### Where to next? Excited to try out Pushpop with your data? Here's a few options to choose from: #### Quickstart Setup Pushpop locally. It takes 10 minutes to get that first shiny report in your inbox, and even less if you already have a Keen IO, Sendgrid or Twilio account. **[Go to the Quickstart](#quickstart)** #### Deploy a Pushpop Instance Ready to deploy the Pushpop job you wrote locally and start getting regular reports? Detailed instructions for Heroku are provided, as well as the basics for other platforms. **[Go to the Deploy Guide](#deploy-guide)** #### Need help? Don't have a hacker at hand? The friendly folks at Keen IO are happy to help you get a Pushpop instance running. **Email [team@keen.io](mailto:team@keen.io?subject=I want a Pushpop!)** with the subject "I want a Pushpop!" ## Quickstart The goal of the Quickstart is to get a Pushpop instance running locally. This should take less than 10 minutes. #### Prerequisites + A working Ruby installation (1.9+) + A [Keen IO](https://keen.io) account and project and associated API keys + A [Sendgrid](https://sendgrid.com) and/or [Twilio](https://twilio.com) account and associated API keys #### Steps **Clone this repository** ``` shell $ git clone git@github.com:keenlabs/pushpop.git ``` Enter the pushpop directory and install dependencies. ``` shell $ cd pushpop $ gem install bundler $ bundle install ``` **Test an example job** There is an example job in [jobs/example_job.rb](jobs/example_job.rb). All it does is print some output to the console. Run this job via a rake task to make sure your configuration is setup properly. ``` shell $ foreman run rake jobs:run_once[jobs/example_job.rb] ``` You should see the following output (followed by a logging statement): ``` html Hey Pushpop, let's do a math!
The number 30!
``` **Specify your API credentials** Now it's time to write a job that connects to APIs and does something real. For that we'll need to specify API keys. We'll use [foreman](https://github.com/ddollar/foreman) to tell Pushpop about these API keys. When you use foreman to run a process, it adds variables from a local `.env` file to the process environment. It's very handy for keeping secure API keys out of your code (`.env` files are gitignored by Pushpop). Create a `.env` file in the project directory and add the API configuration properties and keys that you have. Here's what an example file looks like with settings from all three services: ``` KEEN_PROJECT_ID=********* KEEN_READ_KEY=********* SENDGRID_DOMAIN=********* SENDGRID_PASSWORD=********* SENDGRID_USERNAME=********* TWILIO_AUTH_TOKEN=********* TWILIO_FROM=********* TWILIO_SID=********* ``` **Write your first job** Let's write a job that performs a count of one of your Keen IO collections and sends an email (or SMS) with the result. We'll set it to run every 24 hours. Create a file in the `jobs` folder called `first_job.rb` and paste in the following example: ``` ruby job do # how frequently do we want this job to run? every 24.hours # what keen io query should be performed? keen do event_collection '' analysis_type 'count' timeframe 'last_24_hours' end # use this block to send an email sendgrid do |_, step_responses| to '' from '' subject "There were #{step_responses['keen']} events in the last 24 hours!" body 'Blowing up!' end # use this block to send an sms twilio do |_, step_responses| to '' body "There were #{step_responses['keen']} events in the last 24 hours!" end end ``` Now modify the example to use your specific information. You'll want to specify a `to` and a `from` address if you're using Sendgrid, and a `to` phone number if you're using Twilio. Everything you need to change is marked with `<>`. You'll also want to remove either Sendgrid or Twilio blocks you're not using them. Save the file and test this job using the same `jobs:run_once` rake task that we used before. ``` shell $ foreman run rake jobs:run_once[jobs/first_job.rb] ``` The output of each step will be logged to the console, and if everything worked you'll receive an email or a text message within a few seconds! **Next steps** + Write and test more jobs. See the [Pushpop API Documentation](#pushpop-api-documentation) below for more examples of what you can do. + Continue on to the deploy guide to deploy a Pushpop instance and start getting regular reports and alerts. ## Deploy Guide ##### Heroku These instructions are for Heroku, but should be relevant to most environments. **Prerequisites** You'll need a Heroku account, and the [Heroku toolbelt](https://toolbelt.heroku.com/) installed. **Create a new Heroku app** Make sure you're inside the Pushpop directory. ``` shell $ heroku create ``` **Commit changes** If you created a new job from the Quickstart guide, you'll want to commit that code before deploying. ``` shell $ git commit -am 'Created my first Pushpop job' ``` **Set Heroku config variables** The easiest way to do this is with the [heroku-config](https://github.com/ddollar/heroku-config) plugin. This step assumes you have created a `.env` file containing your keys as demonstrated in the Quickstart guide. ``` shell $ heroku plugins:install git://github.com/ddollar/heroku-config.git $ heroku config:push ``` **Deploy code to Heroku** Now that your code is commited and config variables pushed we can begin a deploy. ``` shell $ git push heroku master ``` **Tail logs to confirm it's working** To see that jobs are running and that there are no errors, tail the logs on Heroku. ``` shell $ heroku logs --tail ``` Note that if you have jobs that are set to run at specific times of day you might not see output for a while. Also note - by default this will run all jobs in the `jobs` folder. You might want to delete the `example_job.rb` file in a separate commit once you've got the hang of things. ##### Other environments Pushpop is run entirely by one long-running Ruby process. Anywhere you can run this process in a monitored fashion you can run a Pushpop instance. Here's the command: ``` shell $ foreman run rake jobs:run ``` If you don't want to use foreman and prefer to set the environment variables yourself then all you need is this: ``` shell $ bundle exec rake jobs:run ``` ## Rake Tasks All `jobs:*` rake tasks optionally take a single filename as a parameter. The file is meant to contain one or more Pushpop jobs. If no filename is specified, all jobs in the jobs folder are considered. Specifying a specific file looks like this: ``` shell $ foreman run rake jobs:run[jobs/just_this_job.rb] ``` Here's a list of the available rake tasks: + `jobs:describe` - Print out the names of jobs in the jobs folder. + `jobs:run_once` - Run each job once, right now. + `jobs:run` - Run jobs as scheduled in a long-running process. This is the task used when you deploy. + `spec` - Run the specs. ## Pushpop API Documentation Steps and jobs are the heart of the Pushpop workflow. Ruby jobs files contain one or more jobs, and each job consists of one or more steps. #### Jobs Jobs have the following attributes: + `name`: (optional) something that describe the job, useful in logs + `every_duration`: the frequency at which to run the job + `every_options` (optional): options related to when the job runs + `steps`: the ordered list of steps to run These attributes are easily specified using the DSL's block syntax. Here's an example: ``` ruby job 'print job' do every 5.minutes step do puts "5 minutes later..." end end ``` Inside of a `job` configuration block, steps are added by using the `step` method. They can also be added by using a method registered by a plugin, like `keen` or `twilio`. For more information, see [Plugin Documentation](#plugin-documentation). The frequency of the job is set via the `every` method. This is basically a passthrough to Clockwork. Here are some cool things you can do with regard to scheduling: ``` ruby every 5.seconds every 24.hours, at: '12:00' every 24.hours, at: ['00:00', '12:00'] every 24.hours, at: '**:05' every 24.hours, at: '00:00', tz: 'UTC' every 5.seconds, at: '10:**' every 1.week, at: 'Monday 12:30' ``` See the full set of options on the [Clockwork README](https://github.com/tomykaira/clockwork#event-parameters). ##### Job workflow When a job kicks off, steps are run serially in the order they are specified. Each step is invoked with 2 arguments - the response of the step immediately preceding it, and a map of all responses so far. The map is keyed by step name, which defaults to a plugin name if a plugin was used but a step name not specified. Here's an example that shows how the response chain works: ``` ruby job do every 5.minutes step 'one' do 1 end step 'two' do |response| 5 + response end step 'add previous steps' do |response, step_responses| puts response # prints 5 puts step_responses['one'] + step_responses['two'] # prints 6 end end ``` If a `step` returns false, subsequent steps **are not run**. Here's a simple example that illustrates this: ``` ruby job 'lame job' do every 5.minutes step 'one' do false end step 'two' do # never called! end end ``` This behavior is designed to make *conditional* alerting easy. Here's an example of a job that only sends an alert for certain query responses: ``` ruby job do every 1.minute keen do event_collection 'errors' analysis_type 'count' timeframe 'last_1_minute' end step 'notify only if there are errors' do |response| response > 0 end twilio do |step_responses| to '+18005555555' body "There were #{step_responses['keen']} errors in the last minute!" end end ``` In this example, the `twilio` step will only be ran if the `keen` step returned a count greater than 0. #### Steps Steps have the following attributes: + `name`: (optional) something that describes the step. Useful in logs, and is the key in the `step_responses` hash. Defaults to plugin name if a plugin is used + `plugin`: (optional) if the step is backed by a plugin, it's the name of the plugin + `block`: A block that runs to configure the step (when a plugin is used) or run it Steps can be pure Ruby code or leverage a plugin DSL. Plugins are just fancy abstractions for creating steps. Steps have built-in support for ERB templating. This is useful for generating more complex emails and reports. Here's an example that uses a template: ``` ruby sendgrid do |response, step_responses| to 'josh+pushpop@keen.io' from 'pushpopapp+123@keen.io' subject 'Pingpong Daily Response Time Report' body template 'pingpong_report.html.erb', response, step_responses preview false end ``` `template` is a function that renders a template in context of the step responses and returns a string. The first argument is a template file name, located in the `templates` directory by default. The second and third arguments are the `response` and `step_responses` respectively. An optional fourth parameter can be used to change the path templates are looked up in. Here's a very simple template: ``` erb

Daily Report

We got <%= response %> new users today!

``` ## Recipes The community-driven [pushpop-recipes](https://github.com/keenlabs/pushpop-recipes) repository contains jobs and templates for doing common things with Pushpop. Check it out for some inspiration! ## Plugin Documentation Plugins are located at `lib/plugins`. They are loaded automatically. ##### Keen The `keen` plugin gives you a DSL to specify Keen query parameters. When it runs, it passes those parameters to the [keen gem](https://github.com/keenlabs/keen-gem), which in turn runs the query against the Keen IO API. Here's an example that shows most of the options you can specify: ``` ruby job 'daily average response time by check for successful requests in april' do keen do event_collection 'checks' analysis_type 'average' target_property 'request.duration' group_by 'check.name' interval 'daily' timeframe ({ start: '2014-04-01T00:00Z' }) filters [{ property_name: "response.successful", operator: "eq", property_value: true }] end end ``` The `keen` plugin requires that the following environment variables are set: `KEEN_PROJECT_ID` and `KEEN_READ_KEY`. A `steps` method is also supported for [funnels](https://keen.io/docs/data-analysis/funnels/), as well as `analyses` for doing a [multi-analysis](https://keen.io/docs/data-analysis/multi-analysis/). ##### Sendgrid The `sendgrid` plugin gives you a DSL to specify email parameters like subject and body. Here's an example: ``` ruby job 'send an email' do sendgrid do to 'josh+pushpop@keen.io' from 'pushpopapp+123@keen.io' subject 'Is your inbox lonely?' body 'This email was intentionally left blank.' preview false end end ``` The `sendgrid` plugin requires that the following environment variables are set: `SENDGRID_DOMAIN`, `SENDGRID_USERNAME`, and `SENDGRID_PASSWORD`. The `preview` directive is optional and defaults to false. If you set it to true, the email contents will print out to the console, but the email will not be sent. The `body` method can take a string, or it can take the same parameters as `template`, in which case it will render a template to create the body. For example: ``` ruby body 'pingpong_report.html.erb', response, step_responses ``` ##### Twilio The `twilio` plugin provides DSL to specify SMS recipient information as well as the text itself. Here's an example: ``` ruby job 'send a text' do twilio do to '18005555555' body 'Breathe in through the nose, out through the mouth.' end end ``` The `twilio` plugin requires that the following environment variables are set: `TWILIO_AUTH_TOKEN`, `TWILIO_SID`, and `TWILIO_FROM`. ## Creating plugins Plugins are just subclasses of `Pushpop::Step`. Plugins should implement a run method, and register themselves. Here's a simple plugin that stops job execution if the input into the step is 0: ``` ruby module Pushpop class BreakIfZero < Step PLUGIN_NAME = 'break_if_zero' def run(last_response=nil, step_responses=nil) last_response == 0 end end Pushpop::Job.register_plugin(BreakIfZero::PLUGIN_NAME, BreakIfZero) end # now in your job you can use the break_if_zero step job do step do [0, 1].shuffle.first end break_if_zero step do puts 'made it through!' end end ``` ## Usage as a Ruby gem Pushpop is also a Ruby gem, so you can add it to an existing Ruby project. Here's how to do that: **Install the gem** ``` ruby # bundler gem 'pushpop' # not bundler gem install 'pushpop' ``` **Require job files and run** Once the gem is available, you should be able to load in Pushpop job files. Once a file loads, all of its jobs are ready to be run or scheduled. ``` ruby load 'some_job.rb' # run the jobs once Pushpop.run # or schedule and run the jobs with clockwork Pushpop.schedule Clockwork.manager.run ``` Note that `pushpop` does not declare dependencies other than `clockwork` and `keen`. If you're using Pushpop plugins like Sendgrid or Twilio you'll need to install and require those dependencies explicitly. ## Contributing Issues and pull requests are welcome! **Wishlist** + Add plugins for more data collection and notification services + Add a web interface that shows the job names, job results, and a countdown to the next run + Add a web interface that lets you preview emails in the browser + Beautiful email templates with support for typical Keen IO query responses (groups, series, etc) **Testing** Pushpop has a full set of specs (including plugins). Run them like this: ``` shell $ bundle exec rake spec ```