# reactor.gem ### A Sidekiq-backed pub/sub layer for your Rails app. [![Build Status](https://travis-ci.org/hired/reactor.svg?branch=master)](https://travis-ci.org/hired/reactor) This gem aims to provide the following tools to augment your ActiveRecord & Sidekiq stack. 1. Barebones event API through Sidekiq to publish whatever you want 2. Database-driven API to manage subscribers so that users may rewire whatever you let them (transactional emails, campaigns, etc...) 3. Static/Code-driven API to subscribe a basic ruby block to an event. 4. A new communication pattern between your ActiveRecord models that runs asynchronously through Sidekiq. a. describe model lifecycle events and callbacks with class-level helper methods/DSL ## Installation Add this line to your application's Gemfile: gem 'reactor' And then execute: $ bundle Or install it yourself as: $ gem install reactor ## Usage Well, this is evolving, so it's probably best to go read the specs. ### Barebones API ```ruby Reactor::Event.publish(:event_name, any: 'data', you: 'want') ``` ### ActiveModel extensions #### Publishable Describe lifecycle events like so ```ruby publishes :my_model_created ``` Schedule an event to get published at a specific time. Note: if timestamp is a property on an ActiveRecord::Model then updating that property will re-schedule the firing of the event ```ruby publishes :something_happened, at: :timestamp ``` Schedule an event to get published at a specific time using a method to generate the timestamp and following some other property. In this case the :something_happened event will be fired 72 hours after your model is created. The event will be re-scheduled if created_at is changed. ```ruby def reminder_email_time created_at + 72.hours end publishes :reminder_sent, at: :reminder_email_time, watch: :created_at ``` Scheduled events can check conditionally fire -- eg: in 2 days fire reminder_email if the user hasn't already responded. ```ruby publishes :reminder_sent, at: :reminder_email_time, if: -> { user.responded == false } ``` #### Subscribable You can now bind any block to an event in your models like so ```ruby on_event :any_event do |event| event.target.do_something_about_it! end ``` Static subscribers like these are automatically placed into Sidekiq and executed in the background It's also possible to run a subscriber block in memory like so ```ruby on_event :any_event, in_memory: true do |event| event.target.do_something_about_it_and_make_the_user_wait! end ``` #### ResourceActionable Enforce a strict 1:1 match between your event model and database model with this controller mixin. ```ruby class PetsController < ApplicationController include Reactor::ResourceActionable actionable_resource :@pet # GET /pets # GET /pets.json def index @pets = current_user.pets respond_to do |format| format.html # index.html.erb format.json { render json: @pets } end end def show @pet = current_user.pets.find(params[:id]) respond_to do |format| format.html # index.html.erb format.json { render json: @pet } end end end ``` Now your index action (and any of the other RESTful actions in that controller) will fire a useful event for you to bind to and log. *Important* Reactor::ResourceActionable has one major usage constraints: Your controller *must* have a method called "action_event" with this signature. ```ruby def action_event(name, options = {}) # Here's what ours looks like, but yours may look different. actor = options[:actor] || current_user actor.publish(name, options.merge(default_action_parameters)) #where default_action_parameters includes things like ip_address, referrer, user_agent end ``` Once you write your own action_event to describe your event data model's base attributes, your ResourceActionable endpoints will now fire events that map like so (for the example above):