bustle 0.0.1 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +2 -0
- data/.rspec +1 -0
- data/.travis.yml +4 -0
- data/README.md +37 -40
- data/bustle.gemspec +9 -1
- data/lib/bustle/activities.rb +25 -0
- data/lib/bustle/concerns/by_publisher.rb +11 -0
- data/lib/bustle/concerns/filterable.rb +11 -0
- data/lib/bustle/concerns/for_subscriber.rb +11 -0
- data/lib/bustle/concerns/resource_collection.rb +25 -0
- data/lib/bustle/models/activity.rb +4 -0
- data/lib/bustle/models/publisher.rb +11 -0
- data/lib/bustle/models/subscriber.rb +7 -0
- data/lib/bustle/models/subscription.rb +4 -0
- data/lib/bustle/publishers.rb +7 -0
- data/lib/bustle/storage/active_record/activity.rb +5 -0
- data/lib/bustle/storage/active_record/publisher.rb +5 -0
- data/lib/bustle/storage/active_record/subscriber.rb +5 -0
- data/lib/bustle/storage/active_record/subscription.rb +5 -0
- data/lib/bustle/storage/active_record.rb +5 -0
- data/lib/bustle/subscribers.rb +7 -0
- data/lib/bustle/subscriptions.rb +29 -0
- data/lib/bustle/version.rb +1 -1
- data/lib/bustle.rb +30 -0
- data/specs/bustle/activities_spec.rb +65 -0
- data/specs/bustle/models/activity_spec.rb +9 -0
- data/specs/bustle/models/publisher_spec.rb +17 -0
- data/specs/bustle/models/subscriber_spec.rb +11 -0
- data/specs/bustle/models/subscription_spec.rb +9 -0
- data/specs/bustle/publishers_spec.rb +8 -0
- data/specs/bustle/storage/active_record_spec.rb +23 -0
- data/specs/bustle/subscribers_spec.rb +8 -0
- data/specs/bustle/subscriptions_spec.rb +76 -0
- data/specs/bustle_spec.rb +12 -0
- data/specs/spec_helper.rb +35 -0
- data/specs/support/active_record_tables.rb +45 -0
- data/specs/support/shared_context/app_resources.rb +11 -0
- data/specs/support/shared_examples/has_activities.rb +17 -0
- data/specs/support/shared_examples/model.rb +38 -0
- data/specs/support/shared_examples/resource_collection.rb +24 -0
- metadata +152 -6
- data/lib/specs/spec_helper.rb +0 -8
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--colour
|
data/.travis.yml
ADDED
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# Bustle
|
1
|
+
# Bustle [](http://travis-ci.org/fredwu/bustle) [](https://gemnasium.com/fredwu/bustle) [](https://codeclimate.com/github/fredwu/bustle)
|
2
2
|
|
3
3
|
Activities recording and retrieving using a simple Pub/Sub architecture.
|
4
4
|
|
@@ -7,6 +7,14 @@ The typical use cases are:
|
|
7
7
|
- Timeline (e.g. tracking activities such as posting and commenting for users)
|
8
8
|
- Logging
|
9
9
|
|
10
|
+
The advantages of Bustle are:
|
11
|
+
|
12
|
+
- It is lightweight and simple to use
|
13
|
+
- It is largely self-contained and separated from you core app logic
|
14
|
+
- It works nicely with ActiveRecord out of box
|
15
|
+
- It can be extended to use with other databases
|
16
|
+
- It has full unit test coverage
|
17
|
+
|
10
18
|
## Installation
|
11
19
|
|
12
20
|
Add this line to your application's Gemfile:
|
@@ -23,11 +31,6 @@ First of all, you will need to configure Bustle. If you are using Rails, you can
|
|
23
31
|
|
24
32
|
```ruby
|
25
33
|
Bustle.config do |c|
|
26
|
-
# Specify an intepreter strategy for intepreting app data
|
27
|
-
# i.e. what your application uses for persistence
|
28
|
-
# Bustle ships with an ActiveRecord intepreter strategy
|
29
|
-
c.intepreter = Bustle::Intepreter::ActiveRecord
|
30
|
-
|
31
34
|
# Specify a storage strategy for storing activities
|
32
35
|
# Bustle ships with an ActiveRecord storage strategy
|
33
36
|
c.storage = Bustle::Storage::ActiveRecord
|
@@ -40,34 +43,34 @@ For ActiveRecord, you will need the following migration file:
|
|
40
43
|
class CreateBustleTables < ActiveRecord::Migration
|
41
44
|
def change
|
42
45
|
create_table :bustle_activities do |t|
|
43
|
-
t.string :
|
44
|
-
t.integer :
|
46
|
+
t.string :resource_class
|
47
|
+
t.integer :resource_id
|
45
48
|
t.string :action
|
46
49
|
t.integer :publisher_id, :null => false
|
47
50
|
t.timestamps
|
48
51
|
end
|
49
52
|
|
50
53
|
create_table :bustle_publishers do |t|
|
51
|
-
t.string :
|
52
|
-
t.integer :
|
54
|
+
t.string :resource_class, :null => false
|
55
|
+
t.integer :resource_id, :null => false
|
53
56
|
t.timestamps
|
54
57
|
end
|
55
58
|
|
56
59
|
create_table :bustle_subscribers do |t|
|
57
|
-
t.string :
|
58
|
-
t.integer :
|
60
|
+
t.string :resource_class, :null => false
|
61
|
+
t.integer :resource_id, :null => false
|
59
62
|
t.timestamps
|
60
63
|
end
|
61
64
|
|
62
65
|
create_table :bustle_subscriptions do |t|
|
63
66
|
t.integer :publisher_id, :null => false
|
64
|
-
t.
|
67
|
+
t.integer :subscriber_id, :null => false
|
65
68
|
t.timestamps
|
66
69
|
end
|
67
70
|
|
68
71
|
add_index :bustle_activities, :publisher_id
|
69
|
-
add_index :bustle_publishers, [:
|
70
|
-
add_index :bustle_subscribers, [:
|
72
|
+
add_index :bustle_publishers, [:resource_class, :resource_id], :unique => true
|
73
|
+
add_index :bustle_subscribers, [:resource_class, :resource_id], :unique => true
|
71
74
|
add_index :bustle_subscriptions, :publisher_id
|
72
75
|
add_index :bustle_subscriptions, :subscriber_id
|
73
76
|
add_index :bustle_subscriptions, [:publisher_id, :subscriber_id], :unique => true
|
@@ -116,19 +119,19 @@ Bustle::Publishers.add post
|
|
116
119
|
Bustle::Subscriptions.add bustle_publisher, bustle_subscriber
|
117
120
|
|
118
121
|
# example
|
119
|
-
publisher = Bustle::Publishers.
|
120
|
-
subscriber = Bustle::Subscribers.
|
122
|
+
publisher = Bustle::Publishers.get(Post.first)
|
123
|
+
subscriber = Bustle::Subscribers.get(User.first)
|
121
124
|
Bustle::Subscriptions.add publisher, subscriber
|
122
125
|
```
|
123
126
|
|
124
127
|
#### Find a Subscriber/Publisher/Subscription
|
125
128
|
|
126
129
|
```ruby
|
127
|
-
Bustle::Subscribers.
|
128
|
-
Bustle::Publishers.
|
129
|
-
Bustle::Subscriptions.
|
130
|
-
Bustle::Subscriptions.
|
131
|
-
Bustle::Subscriptions.
|
130
|
+
Bustle::Subscribers.get subscriber
|
131
|
+
Bustle::Publishers.get publisher
|
132
|
+
Bustle::Subscriptions.get bustle_publisher, bustle_subscriber # => Bustle::Subscription
|
133
|
+
Bustle::Subscriptions.by bustle_publisher # => an array of Bustle::Subscription by the publisher
|
134
|
+
Bustle::Subscriptions.for bustle_subscriber # => an array of Bustle::Subscription for the subscriber
|
132
135
|
```
|
133
136
|
|
134
137
|
#### Remove a Subscriber/Publisher/Subscription
|
@@ -142,9 +145,9 @@ Bustle::Subscriptions.remove bustle_publisher, bustle_subscriber
|
|
142
145
|
Or:
|
143
146
|
|
144
147
|
```ruby
|
145
|
-
Bustle::Subscribers.
|
146
|
-
Bustle::Publishers.
|
147
|
-
Bustle::Subscriptions.
|
148
|
+
Bustle::Subscribers.get(subscriber).destroy
|
149
|
+
Bustle::Publishers.get(publisher).destroy
|
150
|
+
Bustle::Subscriptions.get(bustle_publisher, bustle_subscriber).destroy
|
148
151
|
```
|
149
152
|
|
150
153
|
#### Publish an Activity
|
@@ -158,7 +161,7 @@ Bustle::Publisher.publish action, activity
|
|
158
161
|
post = Post.find(1)
|
159
162
|
comment = post.comments.add(:content => "I'm a comment")
|
160
163
|
Bustle::Publishers.add post
|
161
|
-
publisher = Bustle::Publishers.
|
164
|
+
publisher = Bustle::Publishers.get post
|
162
165
|
publisher.publish 'new', comment
|
163
166
|
```
|
164
167
|
|
@@ -172,7 +175,7 @@ Bustle::Activities.for bustle_subscriber
|
|
172
175
|
Bustle::Subscriber.activities
|
173
176
|
|
174
177
|
# example
|
175
|
-
subscriber = Bustle::Subscribers.
|
178
|
+
subscriber = Bustle::Subscribers.get(User.first)
|
176
179
|
subscriber.activities
|
177
180
|
```
|
178
181
|
|
@@ -184,27 +187,21 @@ Bustle::Activities.by bustle_publisher
|
|
184
187
|
Bustle::Publisher.activities
|
185
188
|
|
186
189
|
# example
|
187
|
-
publisher = Bustle::Publishers.
|
190
|
+
publisher = Bustle::Publishers.get(Post.first)
|
188
191
|
publisher.activities
|
189
192
|
```
|
190
193
|
|
191
|
-
|
194
|
+
#### Filtering (for Activities and Subscriptions)
|
192
195
|
|
193
196
|
```ruby
|
194
|
-
Bustle::Activities.
|
197
|
+
Bustle::Activities.filter :key => :value
|
198
|
+
Bustle::Subscriptions.filter :key => :value
|
195
199
|
# or
|
196
|
-
Bustle::Subscriber.activities
|
200
|
+
Bustle::Subscriber.activities :key => :value
|
197
201
|
|
198
202
|
# example
|
199
|
-
subscriber = Bustle::Subscribers.
|
200
|
-
subscriber.activities
|
201
|
-
subscriber.activities.by(publisher).filter(:action => 'new')
|
202
|
-
```
|
203
|
-
|
204
|
-
Activities are normal enumerable objects from your chosen storage, so in ActiveRecord's case, you may use any Arel methods to query the result:
|
205
|
-
|
206
|
-
```ruby
|
207
|
-
subscriber.activities.filter(:action => 'new').order('created_at ASC').limit(10)
|
203
|
+
subscriber = Bustle::Subscribers.get(User.first)
|
204
|
+
subscriber.activities :action => 'new'
|
208
205
|
```
|
209
206
|
|
210
207
|
## License
|
data/bustle.gemspec
CHANGED
@@ -15,7 +15,15 @@ Gem::Specification.new do |gem|
|
|
15
15
|
gem.require_paths = ["lib"]
|
16
16
|
gem.version = Bustle::VERSION
|
17
17
|
|
18
|
+
gem.add_dependency 'activesupport', '>= 3.0'
|
19
|
+
gem.add_dependency 'orm_adapter', '~> 0.1.0'
|
20
|
+
|
18
21
|
gem.add_development_dependency 'rake'
|
19
22
|
gem.add_development_dependency 'simplecov'
|
20
|
-
gem.add_development_dependency '
|
23
|
+
gem.add_development_dependency 'rspec'
|
24
|
+
gem.add_development_dependency 'factory_girl'
|
25
|
+
gem.add_development_dependency 'database_cleaner'
|
26
|
+
gem.add_development_dependency 'pry'
|
27
|
+
gem.add_development_dependency 'activerecord', '>= 3.0'
|
28
|
+
gem.add_development_dependency 'sqlite3'
|
21
29
|
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Bustle
|
2
|
+
class Activities
|
3
|
+
RESOURCE_NAME = 'Bustle::Activity'
|
4
|
+
|
5
|
+
include Concern::Filterable
|
6
|
+
include Concern::ByPublisher
|
7
|
+
|
8
|
+
class << self
|
9
|
+
def add(publisher, action, activity_resource)
|
10
|
+
Activity.to_adapter.create!(
|
11
|
+
:resource_class => activity_resource.class.name,
|
12
|
+
:resource_id => activity_resource.id,
|
13
|
+
:action => action,
|
14
|
+
:publisher_id => publisher.id
|
15
|
+
)
|
16
|
+
end
|
17
|
+
|
18
|
+
def for(subscriber, conditions = {})
|
19
|
+
publisher_ids = Subscriptions.for(subscriber).map(&:publisher_id)
|
20
|
+
|
21
|
+
filter({ :publisher_id => publisher_ids }.merge(conditions))
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Bustle::Concern
|
2
|
+
module ResourceCollection
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
module ClassMethods
|
6
|
+
def add(resource)
|
7
|
+
self::RESOURCE_NAME.constantize.to_adapter.create!(
|
8
|
+
:resource_class => resource.class.name,
|
9
|
+
:resource_id => resource.id
|
10
|
+
)
|
11
|
+
end
|
12
|
+
|
13
|
+
def get(resource)
|
14
|
+
self::RESOURCE_NAME.constantize.to_adapter.find_first(
|
15
|
+
:resource_class => resource.class.name,
|
16
|
+
:resource_id => resource.id
|
17
|
+
)
|
18
|
+
end
|
19
|
+
|
20
|
+
def remove(resource)
|
21
|
+
get(resource).destroy
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
module Bustle
|
2
|
+
class Publisher < "#{Bustle.storage}::Publisher".constantize
|
3
|
+
def publish(action, activity_resource)
|
4
|
+
Activities.add self, action, activity_resource
|
5
|
+
end
|
6
|
+
|
7
|
+
def activities(conditions = {})
|
8
|
+
Activities.by(self, conditions)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Bustle
|
2
|
+
class Subscriptions
|
3
|
+
RESOURCE_NAME = 'Bustle::Subscription'
|
4
|
+
|
5
|
+
include Concern::Filterable
|
6
|
+
include Concern::ByPublisher
|
7
|
+
include Concern::ForSubscriber
|
8
|
+
|
9
|
+
class << self
|
10
|
+
def add(publisher, subscriber)
|
11
|
+
Subscription.to_adapter.create!(
|
12
|
+
:publisher_id => publisher.id,
|
13
|
+
:subscriber_id => subscriber.id
|
14
|
+
)
|
15
|
+
end
|
16
|
+
|
17
|
+
def get(publisher, subscriber)
|
18
|
+
Subscription.to_adapter.find_first(
|
19
|
+
:publisher_id => publisher.id,
|
20
|
+
:subscriber_id => subscriber.id
|
21
|
+
)
|
22
|
+
end
|
23
|
+
|
24
|
+
def remove(publisher, subscriber)
|
25
|
+
get(publisher, subscriber).destroy
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
data/lib/bustle/version.rb
CHANGED
data/lib/bustle.rb
CHANGED
@@ -1 +1,31 @@
|
|
1
|
+
require 'active_support/dependencies'
|
2
|
+
require 'active_support/concern'
|
3
|
+
|
1
4
|
require "bustle/version"
|
5
|
+
require "bustle/concerns/filterable"
|
6
|
+
require "bustle/concerns/by_publisher"
|
7
|
+
require "bustle/concerns/for_subscriber"
|
8
|
+
require "bustle/concerns/resource_collection"
|
9
|
+
require "bustle/activities"
|
10
|
+
require "bustle/publishers"
|
11
|
+
require "bustle/subscribers"
|
12
|
+
require "bustle/subscriptions"
|
13
|
+
|
14
|
+
module Bustle
|
15
|
+
mattr_accessor :storage
|
16
|
+
|
17
|
+
module Storage
|
18
|
+
autoload :ActiveRecord, 'bustle/storage/active_record'
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.config
|
22
|
+
yield self
|
23
|
+
|
24
|
+
require 'bustle/models/activity'
|
25
|
+
require 'bustle/models/publisher'
|
26
|
+
require 'bustle/models/subscriber'
|
27
|
+
require 'bustle/models/subscription'
|
28
|
+
|
29
|
+
require 'orm_adapter'
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
require_relative '../spec_helper'
|
2
|
+
|
3
|
+
module Bustle
|
4
|
+
describe Activities do
|
5
|
+
include_context 'app_resources'
|
6
|
+
|
7
|
+
let(:publisher) { Publishers.add post }
|
8
|
+
let(:publisher2) { Publishers.add user }
|
9
|
+
let(:subscriber) { Subscribers.add post }
|
10
|
+
let(:subscriber2) { Subscribers.add user }
|
11
|
+
|
12
|
+
it "creates an activity" do
|
13
|
+
Activities.add publisher, 'show', comment
|
14
|
+
|
15
|
+
activity = Activity.to_adapter.get(1)
|
16
|
+
activity.resource_class.should == 'Bustle::Dummy::Comment'
|
17
|
+
activity.resource_id.should == 101
|
18
|
+
activity.action.should == 'show'
|
19
|
+
activity.publisher_id.should == publisher.id
|
20
|
+
end
|
21
|
+
|
22
|
+
context "multiple activities" do
|
23
|
+
before do
|
24
|
+
2.times { Activities.add publisher, 'show', comment }
|
25
|
+
1.times { Activities.add publisher, 'reply', comment }
|
26
|
+
2.times { Activities.add publisher2, 'reply', comment }
|
27
|
+
end
|
28
|
+
|
29
|
+
it "finds all activities" do
|
30
|
+
Activities.filter.count.should == 5
|
31
|
+
end
|
32
|
+
|
33
|
+
context "#by" do
|
34
|
+
it "finds all activities by a publisher" do
|
35
|
+
Activities.by(publisher).count.should == 3
|
36
|
+
end
|
37
|
+
|
38
|
+
it "finds all activities filtered by conditions" do
|
39
|
+
Activities.by(publisher, {
|
40
|
+
:action => 'show'
|
41
|
+
}).count.should == 2
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
context "#for" do
|
46
|
+
before do
|
47
|
+
Subscriptions.add publisher, subscriber
|
48
|
+
Subscriptions.add publisher, subscriber2
|
49
|
+
Subscriptions.add publisher2, subscriber2
|
50
|
+
end
|
51
|
+
|
52
|
+
it "finds all activities for a subscriber" do
|
53
|
+
Activities.for(subscriber).count.should == 3
|
54
|
+
Activities.for(subscriber2).count.should == 5
|
55
|
+
end
|
56
|
+
|
57
|
+
it "finds all activities filtered by conditions" do
|
58
|
+
Activities.for(subscriber2, {
|
59
|
+
:publisher_id => publisher2.id
|
60
|
+
}).count.should == 2
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require_relative '../../spec_helper'
|
2
|
+
|
3
|
+
module Bustle
|
4
|
+
describe 'Publisher' do
|
5
|
+
let(:described_class) { Publisher }
|
6
|
+
|
7
|
+
include_context 'app_resources'
|
8
|
+
it_behaves_like 'model'
|
9
|
+
it_behaves_like 'has_activities'
|
10
|
+
|
11
|
+
it "is able to publish an activity" do
|
12
|
+
publisher = Publishers.add post
|
13
|
+
publisher.publish 'reply', comment
|
14
|
+
Activity.to_adapter.get(1).action.should == 'reply'
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require_relative '../../spec_helper'
|
2
|
+
|
3
|
+
module Bustle::Storage
|
4
|
+
describe ActiveRecord do
|
5
|
+
before(:all) do
|
6
|
+
Bustle.config { |c| c.storage = ActiveRecord }
|
7
|
+
end
|
8
|
+
|
9
|
+
it "loads storage model files" do
|
10
|
+
ActiveRecord.const_defined?(:Activity).should == true
|
11
|
+
ActiveRecord.const_defined?(:Publisher).should == true
|
12
|
+
ActiveRecord.const_defined?(:Subscriber).should == true
|
13
|
+
ActiveRecord.const_defined?(:Subscription).should == true
|
14
|
+
end
|
15
|
+
|
16
|
+
it "has correct table names" do
|
17
|
+
ActiveRecord::Activity.table_name.should == 'bustle_activities'
|
18
|
+
ActiveRecord::Publisher.table_name.should == 'bustle_publishers'
|
19
|
+
ActiveRecord::Subscriber.table_name.should == 'bustle_subscribers'
|
20
|
+
ActiveRecord::Subscription.table_name.should == 'bustle_subscriptions'
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
require_relative '../spec_helper'
|
2
|
+
|
3
|
+
module Bustle
|
4
|
+
describe Subscriptions do
|
5
|
+
include_context 'app_resources'
|
6
|
+
|
7
|
+
let(:publisher) { Publishers.add post }
|
8
|
+
let(:publisher2) { Publishers.add Dummy::Post.create!(:title => '') }
|
9
|
+
let(:subscriber) { Subscribers.add user }
|
10
|
+
let(:subscriber2) { Subscribers.add Dummy::User.create!(:name => '') }
|
11
|
+
|
12
|
+
before do
|
13
|
+
Subscriptions.add publisher, subscriber
|
14
|
+
end
|
15
|
+
|
16
|
+
it "creates a subscription" do
|
17
|
+
subscription = Subscription.to_adapter.get(1)
|
18
|
+
|
19
|
+
subscription.publisher_id.should == publisher.id
|
20
|
+
subscription.subscriber_id.should == subscriber.id
|
21
|
+
end
|
22
|
+
|
23
|
+
it "finds a subscription" do
|
24
|
+
subscription = Subscriptions.get publisher, subscriber
|
25
|
+
|
26
|
+
subscription.publisher_id.should == publisher.id
|
27
|
+
subscription.subscriber_id.should == subscriber.id
|
28
|
+
end
|
29
|
+
|
30
|
+
it "removes a subscription" do
|
31
|
+
subscription = Subscriptions.get publisher, subscriber
|
32
|
+
|
33
|
+
Subscriptions.remove publisher, subscriber
|
34
|
+
Subscriptions.filter.count.should == 0
|
35
|
+
end
|
36
|
+
|
37
|
+
context "finding multiple subscriptions" do
|
38
|
+
before do
|
39
|
+
Subscriptions.add publisher2, subscriber
|
40
|
+
Subscriptions.add publisher, subscriber2
|
41
|
+
Subscriptions.add publisher2, subscriber2
|
42
|
+
end
|
43
|
+
|
44
|
+
it "finds all subscriptions" do
|
45
|
+
Subscriptions.filter.count.should == 4
|
46
|
+
end
|
47
|
+
|
48
|
+
it "finds specific subscriptions" do
|
49
|
+
Subscriptions.filter({
|
50
|
+
:publisher_id => publisher.id,
|
51
|
+
:subscriber_id => subscriber.id
|
52
|
+
}).count.should == 1
|
53
|
+
end
|
54
|
+
|
55
|
+
it "finds subscriptions by a publisher" do
|
56
|
+
Subscriptions.by(publisher).count.should == 2
|
57
|
+
end
|
58
|
+
|
59
|
+
it "finds subscriptions by a publisher and conditions" do
|
60
|
+
Subscriptions.by(publisher, {
|
61
|
+
:subscriber_id => subscriber2.id
|
62
|
+
}).count.should == 1
|
63
|
+
end
|
64
|
+
|
65
|
+
it "finds subscriptions for a subscriber" do
|
66
|
+
Subscriptions.for(subscriber).count.should == 2
|
67
|
+
end
|
68
|
+
|
69
|
+
it "finds subscriptions for a subscriber and conditions" do
|
70
|
+
Subscriptions.for(subscriber, {
|
71
|
+
:publisher_id => publisher.id
|
72
|
+
}).count.should == 1
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
require_relative 'spec_helper'
|
2
|
+
|
3
|
+
describe Bustle do
|
4
|
+
context "configuration" do
|
5
|
+
[:storage].each do |option|
|
6
|
+
it "sets the config option #{option}" do
|
7
|
+
Bustle.config { |c| c.send "#{option}=", 'dummy' }
|
8
|
+
Bustle.send(option).should == 'dummy'
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'simplecov'
|
2
|
+
SimpleCov.start
|
3
|
+
|
4
|
+
require 'rspec/autorun'
|
5
|
+
require 'factory_girl'
|
6
|
+
require 'database_cleaner'
|
7
|
+
require 'pry'
|
8
|
+
|
9
|
+
require File.expand_path('../../lib/bustle', __FILE__)
|
10
|
+
Dir[File.expand_path('../../specs/support/**/*.rb', __FILE__)].each {|f| require f}
|
11
|
+
|
12
|
+
BUSTLE_STORAGES = [
|
13
|
+
Bustle::Storage::ActiveRecord
|
14
|
+
]
|
15
|
+
|
16
|
+
RSpec.configure do |config|
|
17
|
+
config.include FactoryGirl::Syntax::Methods
|
18
|
+
|
19
|
+
config.before(:suite) do
|
20
|
+
module Bustle::Dummy
|
21
|
+
class User < ActiveRecord::Base; end
|
22
|
+
class Post < ActiveRecord::Base; end
|
23
|
+
class Comment < ActiveRecord::Base; end
|
24
|
+
end
|
25
|
+
|
26
|
+
Bustle.config do |c|
|
27
|
+
c.storage = Bustle::Storage::ActiveRecord
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
DatabaseCleaner.strategy = :truncation
|
32
|
+
config.before do
|
33
|
+
DatabaseCleaner.clean
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'active_record'
|
2
|
+
|
3
|
+
ActiveRecord::Base.establish_connection(:adapter => 'sqlite3', :database => ":memory:")
|
4
|
+
|
5
|
+
ActiveRecord::Migration.suppress_messages do
|
6
|
+
ActiveRecord::Schema.define(:version => 0) do
|
7
|
+
create_table :users, :force => true do |t|
|
8
|
+
t.string :name
|
9
|
+
end
|
10
|
+
|
11
|
+
create_table :posts, :force => true do |t|
|
12
|
+
t.string :title
|
13
|
+
end
|
14
|
+
|
15
|
+
create_table :comments, :force => true do |t|
|
16
|
+
t.string :title
|
17
|
+
end
|
18
|
+
|
19
|
+
create_table :bustle_activities, :force => true do |t|
|
20
|
+
t.string :resource_class
|
21
|
+
t.integer :resource_id
|
22
|
+
t.string :action
|
23
|
+
t.integer :publisher_id
|
24
|
+
t.timestamps
|
25
|
+
end
|
26
|
+
|
27
|
+
create_table :bustle_publishers, :force => true do |t|
|
28
|
+
t.string :resource_class
|
29
|
+
t.integer :resource_id
|
30
|
+
t.timestamps
|
31
|
+
end
|
32
|
+
|
33
|
+
create_table :bustle_subscribers, :force => true do |t|
|
34
|
+
t.string :resource_class
|
35
|
+
t.integer :resource_id
|
36
|
+
t.timestamps
|
37
|
+
end
|
38
|
+
|
39
|
+
create_table :bustle_subscriptions, :force => true do |t|
|
40
|
+
t.integer :publisher_id
|
41
|
+
t.integer :subscriber_id
|
42
|
+
t.timestamps
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
shared_context 'app_resources' do
|
2
|
+
let(:user) { Bustle::Dummy::User.create! :name => 'Fred' }
|
3
|
+
let(:post) { Bustle::Dummy::Post.create! :title => 'hello' }
|
4
|
+
let(:comment) { Bustle::Dummy::Comment.create! :title => 'hi' }
|
5
|
+
|
6
|
+
before do
|
7
|
+
user.update_attribute :id, 42
|
8
|
+
post.update_attribute :id, 69
|
9
|
+
comment.update_attribute :id, 101
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
shared_examples 'has_activities' do
|
2
|
+
context "activities" do
|
3
|
+
let(:publisher) { Bustle::Publishers.add post }
|
4
|
+
let(:subscriber) { Bustle::Subscribers.add user }
|
5
|
+
|
6
|
+
before do
|
7
|
+
Bustle::Subscriptions.add publisher, subscriber
|
8
|
+
end
|
9
|
+
|
10
|
+
it "has activities" do
|
11
|
+
2.times { publisher.publish 'show', comment }
|
12
|
+
|
13
|
+
publisher.activities.count.should == 2
|
14
|
+
subscriber.activities.count.should == 2
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
shared_examples 'model' do
|
2
|
+
BUSTLE_STORAGES.each do |storage|
|
3
|
+
let(:factory_class) { described_class }
|
4
|
+
let(:factory_name) { factory_class.to_s.split('::').last.downcase.to_sym }
|
5
|
+
|
6
|
+
before(:all) do
|
7
|
+
Bustle.config { |c| c.storage = storage }
|
8
|
+
|
9
|
+
_factory_class = described_class
|
10
|
+
_factory_name = factory_name
|
11
|
+
|
12
|
+
FactoryGirl.define do
|
13
|
+
factory _factory_name, :class => _factory_class do
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
context "storage #{storage}" do
|
19
|
+
it "has correct parent classes" do
|
20
|
+
described_class.superclass.to_s.should == "#{Bustle.storage}::#{described_class.name.split('::').last}"
|
21
|
+
end
|
22
|
+
|
23
|
+
it "is able to create a new instance" do
|
24
|
+
described_class.to_adapter.respond_to?(:create!).should == true
|
25
|
+
end
|
26
|
+
|
27
|
+
it "is able to destroy itself" do
|
28
|
+
described_class.new.respond_to?(:destroy).should == true
|
29
|
+
|
30
|
+
create factory_name
|
31
|
+
described_class.to_adapter.find_all({}).count.should == 1
|
32
|
+
|
33
|
+
described_class.to_adapter.find_first({}).destroy
|
34
|
+
described_class.to_adapter.find_all({}).count.should == 0
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
shared_examples 'resource_collection' do
|
2
|
+
let(:resource_class) { described_class::RESOURCE_NAME.constantize.to_adapter }
|
3
|
+
|
4
|
+
before do
|
5
|
+
described_class.add user
|
6
|
+
end
|
7
|
+
|
8
|
+
it "adds a model record" do
|
9
|
+
model = resource_class.get(1)
|
10
|
+
model.resource_class.should == 'Bustle::Dummy::User'
|
11
|
+
model.resource_id.should == 42
|
12
|
+
end
|
13
|
+
|
14
|
+
it "finds a model record" do
|
15
|
+
model = described_class.get user
|
16
|
+
model.resource_class.should == 'Bustle::Dummy::User'
|
17
|
+
model.resource_id.should == 42
|
18
|
+
end
|
19
|
+
|
20
|
+
it "removes a model record" do
|
21
|
+
described_class.remove(user)
|
22
|
+
resource_class.find_all({}).count.should == 0
|
23
|
+
end
|
24
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: bustle
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,8 +9,40 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-06-
|
12
|
+
date: 2012-06-13 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: activesupport
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '3.0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '3.0'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: orm_adapter
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ~>
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: 0.1.0
|
38
|
+
type: :runtime
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ~>
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: 0.1.0
|
14
46
|
- !ruby/object:Gem::Dependency
|
15
47
|
name: rake
|
16
48
|
requirement: !ruby/object:Gem::Requirement
|
@@ -44,7 +76,87 @@ dependencies:
|
|
44
76
|
- !ruby/object:Gem::Version
|
45
77
|
version: '0'
|
46
78
|
- !ruby/object:Gem::Dependency
|
47
|
-
name:
|
79
|
+
name: rspec
|
80
|
+
requirement: !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
83
|
+
- - ! '>='
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: '0'
|
86
|
+
type: :development
|
87
|
+
prerelease: false
|
88
|
+
version_requirements: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ! '>='
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: '0'
|
94
|
+
- !ruby/object:Gem::Dependency
|
95
|
+
name: factory_girl
|
96
|
+
requirement: !ruby/object:Gem::Requirement
|
97
|
+
none: false
|
98
|
+
requirements:
|
99
|
+
- - ! '>='
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: '0'
|
102
|
+
type: :development
|
103
|
+
prerelease: false
|
104
|
+
version_requirements: !ruby/object:Gem::Requirement
|
105
|
+
none: false
|
106
|
+
requirements:
|
107
|
+
- - ! '>='
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '0'
|
110
|
+
- !ruby/object:Gem::Dependency
|
111
|
+
name: database_cleaner
|
112
|
+
requirement: !ruby/object:Gem::Requirement
|
113
|
+
none: false
|
114
|
+
requirements:
|
115
|
+
- - ! '>='
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
none: false
|
122
|
+
requirements:
|
123
|
+
- - ! '>='
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
version: '0'
|
126
|
+
- !ruby/object:Gem::Dependency
|
127
|
+
name: pry
|
128
|
+
requirement: !ruby/object:Gem::Requirement
|
129
|
+
none: false
|
130
|
+
requirements:
|
131
|
+
- - ! '>='
|
132
|
+
- !ruby/object:Gem::Version
|
133
|
+
version: '0'
|
134
|
+
type: :development
|
135
|
+
prerelease: false
|
136
|
+
version_requirements: !ruby/object:Gem::Requirement
|
137
|
+
none: false
|
138
|
+
requirements:
|
139
|
+
- - ! '>='
|
140
|
+
- !ruby/object:Gem::Version
|
141
|
+
version: '0'
|
142
|
+
- !ruby/object:Gem::Dependency
|
143
|
+
name: activerecord
|
144
|
+
requirement: !ruby/object:Gem::Requirement
|
145
|
+
none: false
|
146
|
+
requirements:
|
147
|
+
- - ! '>='
|
148
|
+
- !ruby/object:Gem::Version
|
149
|
+
version: '3.0'
|
150
|
+
type: :development
|
151
|
+
prerelease: false
|
152
|
+
version_requirements: !ruby/object:Gem::Requirement
|
153
|
+
none: false
|
154
|
+
requirements:
|
155
|
+
- - ! '>='
|
156
|
+
- !ruby/object:Gem::Version
|
157
|
+
version: '3.0'
|
158
|
+
- !ruby/object:Gem::Dependency
|
159
|
+
name: sqlite3
|
48
160
|
requirement: !ruby/object:Gem::Requirement
|
49
161
|
none: false
|
50
162
|
requirements:
|
@@ -67,13 +179,47 @@ extensions: []
|
|
67
179
|
extra_rdoc_files: []
|
68
180
|
files:
|
69
181
|
- .gitignore
|
182
|
+
- .rspec
|
183
|
+
- .travis.yml
|
70
184
|
- Gemfile
|
71
185
|
- README.md
|
72
186
|
- Rakefile
|
73
187
|
- bustle.gemspec
|
74
188
|
- lib/bustle.rb
|
189
|
+
- lib/bustle/activities.rb
|
190
|
+
- lib/bustle/concerns/by_publisher.rb
|
191
|
+
- lib/bustle/concerns/filterable.rb
|
192
|
+
- lib/bustle/concerns/for_subscriber.rb
|
193
|
+
- lib/bustle/concerns/resource_collection.rb
|
194
|
+
- lib/bustle/models/activity.rb
|
195
|
+
- lib/bustle/models/publisher.rb
|
196
|
+
- lib/bustle/models/subscriber.rb
|
197
|
+
- lib/bustle/models/subscription.rb
|
198
|
+
- lib/bustle/publishers.rb
|
199
|
+
- lib/bustle/storage/active_record.rb
|
200
|
+
- lib/bustle/storage/active_record/activity.rb
|
201
|
+
- lib/bustle/storage/active_record/publisher.rb
|
202
|
+
- lib/bustle/storage/active_record/subscriber.rb
|
203
|
+
- lib/bustle/storage/active_record/subscription.rb
|
204
|
+
- lib/bustle/subscribers.rb
|
205
|
+
- lib/bustle/subscriptions.rb
|
75
206
|
- lib/bustle/version.rb
|
76
|
-
-
|
207
|
+
- specs/bustle/activities_spec.rb
|
208
|
+
- specs/bustle/models/activity_spec.rb
|
209
|
+
- specs/bustle/models/publisher_spec.rb
|
210
|
+
- specs/bustle/models/subscriber_spec.rb
|
211
|
+
- specs/bustle/models/subscription_spec.rb
|
212
|
+
- specs/bustle/publishers_spec.rb
|
213
|
+
- specs/bustle/storage/active_record_spec.rb
|
214
|
+
- specs/bustle/subscribers_spec.rb
|
215
|
+
- specs/bustle/subscriptions_spec.rb
|
216
|
+
- specs/bustle_spec.rb
|
217
|
+
- specs/spec_helper.rb
|
218
|
+
- specs/support/active_record_tables.rb
|
219
|
+
- specs/support/shared_context/app_resources.rb
|
220
|
+
- specs/support/shared_examples/has_activities.rb
|
221
|
+
- specs/support/shared_examples/model.rb
|
222
|
+
- specs/support/shared_examples/resource_collection.rb
|
77
223
|
homepage: https://github.com/fredwu/bustle
|
78
224
|
licenses: []
|
79
225
|
post_install_message:
|
@@ -88,7 +234,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
88
234
|
version: '0'
|
89
235
|
segments:
|
90
236
|
- 0
|
91
|
-
hash:
|
237
|
+
hash: 1631565603439208655
|
92
238
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
93
239
|
none: false
|
94
240
|
requirements:
|
@@ -97,7 +243,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
97
243
|
version: '0'
|
98
244
|
segments:
|
99
245
|
- 0
|
100
|
-
hash:
|
246
|
+
hash: 1631565603439208655
|
101
247
|
requirements: []
|
102
248
|
rubyforge_project:
|
103
249
|
rubygems_version: 1.8.24
|