Skip to content

Commit

Permalink
Initial commit - Readme Driven Development
Browse files Browse the repository at this point in the history
  • Loading branch information
fredwu committed Jun 10, 2012
0 parents commit 0a0ba6c
Show file tree
Hide file tree
Showing 8 changed files with 281 additions and 0 deletions.
17 changes: 17 additions & 0 deletions .gitignore
@@ -0,0 +1,17 @@
*.gem
*.rbc
.bundle
.config
.yardoc
Gemfile.lock
InstalledFiles
_yardoc
coverage
doc/
lib/bundler/man
pkg
rdoc
spec/reports
test/tmp
test/version_tmp
tmp
4 changes: 4 additions & 0 deletions Gemfile
@@ -0,0 +1,4 @@
source 'https://rubygems.org'

# Specify your gem's dependencies in bustle.gemspec
gemspec
216 changes: 216 additions & 0 deletions README.md
@@ -0,0 +1,216 @@
# Bustle

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

## Installation

Add this line to your application's Gemfile:

```ruby
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`).

```ruby
Bustle.config do |c|
# Specify an intepreter strategy for intepreting app data
# i.e. what your application uses for persistence
# Bustle ships with an ActiveRecord intepreter strategy
c.intepreter = Bustle::Intepreter::ActiveRecord

# 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:

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

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

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

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

add_index :bustle_activities, :publisher_id
add_index :bustle_publishers, [:reference_class, :reference_id], :unique => true
add_index :bustle_subscribers, [:reference_class, :reference_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

#### Register a Subscriber

```ruby
Bustle::Subscribers.add subscriber

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

#### Register a Publisher

```ruby
Bustle::Publishers.add publisher

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

#### Create a Subscription

```ruby
Bustle::Subscriptions.add bustle_publisher, bustle_subscriber

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

#### Find a Subscriber/Publisher/Subscription

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

#### Remove a Subscriber/Publisher/Subscription

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

Or:

```ruby
Bustle::Subscribers.find(subscriber).destroy
Bustle::Publishers.find(publisher).destroy
Bustle::Subscriptions.find(bustle_publisher, bustle_subscriber).destroy
```

#### Publish an Activity

```ruby
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.find post
publisher.publish 'new', comment
```

#### Activities

##### Retrieve Activities for a Subscriber

```ruby
Bustle::Activities.for bustle_subscriber
# or
Bustle::Subscriber.activities

# example
subscriber = Bustle::Subscribers.find(User.first)
subscriber.activities
```

##### Retrieve Activities by a Publisher

```ruby
Bustle::Activities.by bustle_publisher
# or
Bustle::Publisher.activities

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

##### Activities Filtering

```ruby
Bustle::Activities.for(bustle_subscriber).filter :key => :value
# or
Bustle::Subscriber.activities.filter :key => :value

# example
subscriber = Bustle::Subscribers.find(User.first)
subscriber.activities.filter :action => 'new'
subscriber.activities.by(publisher).filter(:action => 'new')
```

Activities are normal enumerable objects from your chosen storage, so in ActiveRecord's case, you may use any Arel methods to query the result:

```ruby
subscriber.activities.filter(:action => 'new').order('created_at ASC').limit(10)
```

## License

This gem is released under the [MIT License](http://www.opensource.org/licenses/mit-license.php).

## Author

[Fred Wu](https://github.com/fredwu), originally built for [500 Startups](http://500.co).
11 changes: 11 additions & 0 deletions Rakefile
@@ -0,0 +1,11 @@
#!/usr/bin/env rake
require "bundler/gem_tasks"
require 'rake/testtask'

Rake::TestTask.new do |t|
t.libs.push 'lib'
t.test_files = FileList['specs/**/*_spec.rb']
t.verbose = true
end

task :default => :test
21 changes: 21 additions & 0 deletions bustle.gemspec
@@ -0,0 +1,21 @@
# -*- encoding: utf-8 -*-
require File.expand_path('../lib/bustle/version', __FILE__)

Gem::Specification.new do |gem|
gem.authors = ["Fred Wu"]
gem.email = ["ifredwu@gmail.com"]
gem.description = %q{Activities recording and retrieving using a simple Pub/Sub architecture.}
gem.summary = gem.description
gem.homepage = "https://github.com/fredwu/bustle"

gem.files = `git ls-files`.split($\)
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
gem.name = "bustle"
gem.require_paths = ["lib"]
gem.version = Bustle::VERSION

gem.add_development_dependency 'rake'
gem.add_development_dependency 'simplecov'
gem.add_development_dependency 'minitest-colorize'
end
1 change: 1 addition & 0 deletions lib/bustle.rb
@@ -0,0 +1 @@
require "bustle/version"
3 changes: 3 additions & 0 deletions lib/bustle/version.rb
@@ -0,0 +1,3 @@
module Bustle
VERSION = "0.0.1"
end
8 changes: 8 additions & 0 deletions lib/specs/spec_helper.rb
@@ -0,0 +1,8 @@
require 'simplecov'
SimpleCov.start

require 'minitest-colorize'
require 'minitest/autorun'
require 'minitest/spec'

require File.expand_path('../../lib/bustle', __FILE__)

0 comments on commit 0a0ba6c

Please sign in to comment.