Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
branch: master
Fetching contributors…

Octocat-spinner-32-eaf2f5

Cannot retrieve contributors at this time

file 243 lines (183 sloc) 6.315 kb

Bustle Build Status Dependency Status Code Climate

Activities recording and retrieving using a simple Pub/Sub architecture.

The typical use cases are:

  • Timeline (e.g. tracking activities such as posting and commenting for users)
  • Logging

The advantages of Bustle are:

  • It is lightweight and simple to use
  • It is largely self-contained and separated from you core app logic
  • It works nicely with ActiveRecord out of box
  • It can be extended to use with other databases
  • It has full unit test coverage

Installation

Add this line to your application's Gemfile:

gem 'bustle'

Usage

Configuration

First of all, you will need to configure Bustle. If you are using Rails, you can put the following code in an initializer (e.g. config/initializers/bustle.rb).

Bustle.config do |c|
  # Specify a storage strategy for storing activities
  # Bustle ships with an ActiveRecord storage strategy
  c.storage = Bustle::Storage::ActiveRecord
end

For ActiveRecord, you will need the following migration file:

class CreateBustleTables < ActiveRecord::Migration
  def change
    create_table :bustle_activities do |t|
      t.string  :resource_class
      t.integer :resource_id
      t.string  :action
      t.integer :publisher_id, :null => false
      t.timestamps
    end

    create_table :bustle_publishers do |t|
      t.string  :resource_class, :null => false
      t.integer :resource_id,    :null => false
      t.timestamps
    end

    create_table :bustle_subscribers do |t|
      t.string  :resource_class, :null => false
      t.integer :resource_id,    :null => false
      t.timestamps
    end

    create_table :bustle_subscriptions do |t|
      t.integer :publisher_id,  :null => false
      t.integer :subscriber_id, :null => false
      t.timestamps
    end

    add_index :bustle_activities, :publisher_id
    add_index :bustle_publishers, [:resource_class, :resource_id], :unique => true
    add_index :bustle_subscribers, [:resource_class, :resource_id], :unique => true
    add_index :bustle_subscriptions, :publisher_id
    add_index :bustle_subscriptions, :subscriber_id
    add_index :bustle_subscriptions, [:publisher_id, :subscriber_id], :unique => true
  end
end

Flow

Upon subscribing:

  1. Subscriber registers itself if not already registered
  2. Publisher registers itself if not already registered
  3. A Subscription is created for Subscriber and Publisher

When activities occur:

  1. Publisher registers itself if not already registered
  2. Publisher publishes activity

API

Completion Status

Complete list of APIs to build:

  • [x] Bustle::Publisher.publish
  • [x] Bustle::Activities.add
  • [x] Bustle::Subscribers.add
  • [x] Bustle::Publishers.add
  • [x] Bustle::Subscriptions.add
  • [x] Bustle::Subscribers.get
  • [x] Bustle::Publishers.get
  • [x] Bustle::Subscriptions.get
  • [x] Bustle::Subscriptions.for
  • [x] Bustle::Subscriptions.by
  • [x] Bustle::Subscriptions.filter
  • [x] Bustle::Subscribers.remove
  • [x] Bustle::Publishers.remove
  • [x] Bustle::Subscriptions.remove
  • [x] Bustle::Activity.destroy
  • [x] Bustle::Subscriber.destroy
  • [x] Bustle::Publisher.destroy
  • [x] Bustle::Subscription.destroy
  • [x] Bustle::Activities.for
  • [ ] Bustle::Subscriber.activities
  • [x] Bustle::Activities.by
  • [ ] Bustle::Publisher.activities
  • [ ] Bustle::Activities.filter
  • [ ] Bustle::Activities.delegate

Register a Subscriber

Bustle::Subscribers.add subscriber

# example
user = User.find(1)
Bustle::Subscribers.add user

Register a Publisher

Bustle::Publishers.add publisher

# example
post = Post.find(1)
Bustle::Publishers.add post

Create a Subscription

Bustle::Subscriptions.add bustle_publisher, bustle_subscriber

# example
publisher  = Bustle::Publishers.get(Post.first)
subscriber = Bustle::Subscribers.get(User.first)
Bustle::Subscriptions.add publisher, subscriber

Find a Subscriber/Publisher/Subscription

Bustle::Subscribers.get subscriber
Bustle::Publishers.get publisher
Bustle::Subscriptions.get bustle_publisher, bustle_subscriber # => Bustle::Subscription
Bustle::Subscriptions.by bustle_publisher # => an array of Bustle::Subscription by the publisher
Bustle::Subscriptions.for bustle_subscriber # => an array of Bustle::Subscription for the subscriber

Remove a Subscriber/Publisher/Subscription

Bustle::Subscribers.remove subscriber
Bustle::Publishers.remove publisher
Bustle::Subscriptions.remove bustle_publisher, bustle_subscriber

Or:

Bustle::Subscribers.get(subscriber).destroy
Bustle::Publishers.get(publisher).destroy
Bustle::Subscriptions.get(bustle_publisher, bustle_subscriber).destroy

Publish an Activity

Bustle::Activities.add bustle_publisher, action, activity
# or
Bustle::Publisher.publish action, activity

# example
post    = Post.find(1)
comment = post.comments.add(:content => "I'm a comment")
Bustle::Publishers.add post
publisher = Bustle::Publishers.get post
publisher.publish 'new', comment

Activities

Retrieve Activities for a Subscriber
Bustle::Activities.for bustle_subscriber
# or
Bustle::Subscriber.activities

# example
subscriber = Bustle::Subscribers.get(User.first)
subscriber.activities
Retrieve Activities by a Publisher
Bustle::Activities.by bustle_publisher
# or
Bustle::Publisher.activities

# example
publisher = Bustle::Publishers.get(Post.first)
publisher.activities

Filtering (for Activities and Subscriptions)

Bustle::Activities.filter :key => :value
Bustle::Subscriptions.filter :key => :value
# or
Bustle::Subscriber.activities :key => :value

# example
subscriber = Bustle::Subscribers.get(User.first)
subscriber.activities :action => 'new'

License

This gem is released under the MIT License.

Author

Fred Wu, originally built for 500 Startups.

Something went wrong with that request. Please try again.