bustle 0.0.1 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. data/.gitignore +2 -0
  2. data/.rspec +1 -0
  3. data/.travis.yml +4 -0
  4. data/README.md +37 -40
  5. data/bustle.gemspec +9 -1
  6. data/lib/bustle/activities.rb +25 -0
  7. data/lib/bustle/concerns/by_publisher.rb +11 -0
  8. data/lib/bustle/concerns/filterable.rb +11 -0
  9. data/lib/bustle/concerns/for_subscriber.rb +11 -0
  10. data/lib/bustle/concerns/resource_collection.rb +25 -0
  11. data/lib/bustle/models/activity.rb +4 -0
  12. data/lib/bustle/models/publisher.rb +11 -0
  13. data/lib/bustle/models/subscriber.rb +7 -0
  14. data/lib/bustle/models/subscription.rb +4 -0
  15. data/lib/bustle/publishers.rb +7 -0
  16. data/lib/bustle/storage/active_record/activity.rb +5 -0
  17. data/lib/bustle/storage/active_record/publisher.rb +5 -0
  18. data/lib/bustle/storage/active_record/subscriber.rb +5 -0
  19. data/lib/bustle/storage/active_record/subscription.rb +5 -0
  20. data/lib/bustle/storage/active_record.rb +5 -0
  21. data/lib/bustle/subscribers.rb +7 -0
  22. data/lib/bustle/subscriptions.rb +29 -0
  23. data/lib/bustle/version.rb +1 -1
  24. data/lib/bustle.rb +30 -0
  25. data/specs/bustle/activities_spec.rb +65 -0
  26. data/specs/bustle/models/activity_spec.rb +9 -0
  27. data/specs/bustle/models/publisher_spec.rb +17 -0
  28. data/specs/bustle/models/subscriber_spec.rb +11 -0
  29. data/specs/bustle/models/subscription_spec.rb +9 -0
  30. data/specs/bustle/publishers_spec.rb +8 -0
  31. data/specs/bustle/storage/active_record_spec.rb +23 -0
  32. data/specs/bustle/subscribers_spec.rb +8 -0
  33. data/specs/bustle/subscriptions_spec.rb +76 -0
  34. data/specs/bustle_spec.rb +12 -0
  35. data/specs/spec_helper.rb +35 -0
  36. data/specs/support/active_record_tables.rb +45 -0
  37. data/specs/support/shared_context/app_resources.rb +11 -0
  38. data/specs/support/shared_examples/has_activities.rb +17 -0
  39. data/specs/support/shared_examples/model.rb +38 -0
  40. data/specs/support/shared_examples/resource_collection.rb +24 -0
  41. metadata +152 -6
  42. data/lib/specs/spec_helper.rb +0 -8
data/.gitignore CHANGED
@@ -1,3 +1,5 @@
1
+ .DS_Store
2
+ ._*
1
3
  *.gem
2
4
  *.rbc
3
5
  .bundle
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --colour
data/.travis.yml ADDED
@@ -0,0 +1,4 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.2
4
+ - 1.9.3
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # Bustle
1
+ # Bustle [![Build Status](https://secure.travis-ci.org/fredwu/bustle.png?branch=master)](http://travis-ci.org/fredwu/bustle) [![Dependency Status](https://gemnasium.com/fredwu/bustle.png)](https://gemnasium.com/fredwu/bustle) [![Code Climate](https://codeclimate.com/badge.png)](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 :reference_class
44
- t.integer :reference_id
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 :reference_class, :null => false
52
- t.integer :reference_id, :null => false
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 :reference_class, :null => false
58
- t.integer :reference_id, :null => false
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.string :subscriber_id, :null => false
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, [:reference_class, :reference_id], :unique => true
70
- add_index :bustle_subscribers, [:reference_class, :reference_id], :unique => true
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.find(Post.first)
120
- subscriber = Bustle::Subscribers.find(User.first)
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.find subscriber
128
- Bustle::Publishers.find publisher
129
- Bustle::Subscriptions.find bustle_publisher, bustle_subscriber # => Bustle::Subscription
130
- Bustle::Subscriptions.find bustle_publisher # => an array of Bustle::Subscription for the publisher
131
- Bustle::Subscriptions.find bustle_subscriber # => an array of Bustle::Subscription for the subscriber
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.find(subscriber).destroy
146
- Bustle::Publishers.find(publisher).destroy
147
- Bustle::Subscriptions.find(bustle_publisher, bustle_subscriber).destroy
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.find post
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.find(User.first)
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.find(Post.first)
190
+ publisher = Bustle::Publishers.get(Post.first)
188
191
  publisher.activities
189
192
  ```
190
193
 
191
- ##### Activities Filtering
194
+ #### Filtering (for Activities and Subscriptions)
192
195
 
193
196
  ```ruby
194
- Bustle::Activities.for(bustle_subscriber).filter :key => :value
197
+ Bustle::Activities.filter :key => :value
198
+ Bustle::Subscriptions.filter :key => :value
195
199
  # or
196
- Bustle::Subscriber.activities.filter :key => :value
200
+ Bustle::Subscriber.activities :key => :value
197
201
 
198
202
  # example
199
- subscriber = Bustle::Subscribers.find(User.first)
200
- subscriber.activities.filter :action => 'new'
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 'minitest-colorize'
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,11 @@
1
+ module Bustle::Concern
2
+ module ByPublisher
3
+ extend ActiveSupport::Concern
4
+
5
+ module ClassMethods
6
+ def by(publisher, conditions = {})
7
+ filter({ :publisher_id => publisher.id }.merge(conditions))
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ module Bustle::Concern
2
+ module Filterable
3
+ extend ActiveSupport::Concern
4
+
5
+ module ClassMethods
6
+ def filter(options = {})
7
+ self::RESOURCE_NAME.constantize.to_adapter.find_all(options)
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ module Bustle::Concern
2
+ module ForSubscriber
3
+ extend ActiveSupport::Concern
4
+
5
+ module ClassMethods
6
+ def for(subscriber, conditions = {})
7
+ filter({ :subscriber_id => subscriber.id }.merge(conditions))
8
+ end
9
+ end
10
+ end
11
+ 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,4 @@
1
+ module Bustle
2
+ class Activity < "#{Bustle.storage}::Activity".constantize
3
+ end
4
+ 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,7 @@
1
+ module Bustle
2
+ class Subscriber < "#{Bustle.storage}::Subscriber".constantize
3
+ def activities(conditions = {})
4
+ Activities.for(self, conditions)
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,4 @@
1
+ module Bustle
2
+ class Subscription < "#{Bustle.storage}::Subscription".constantize
3
+ end
4
+ end
@@ -0,0 +1,7 @@
1
+ module Bustle
2
+ class Publishers
3
+ RESOURCE_NAME = 'Bustle::Publisher'
4
+
5
+ include Concern::ResourceCollection
6
+ end
7
+ end
@@ -0,0 +1,5 @@
1
+ module Bustle::Storage::ActiveRecord
2
+ class Activity < ::ActiveRecord::Base
3
+ self.table_name = 'bustle_activities'
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ module Bustle::Storage::ActiveRecord
2
+ class Publisher < ::ActiveRecord::Base
3
+ self.table_name = 'bustle_publishers'
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ module Bustle::Storage::ActiveRecord
2
+ class Subscriber < ::ActiveRecord::Base
3
+ self.table_name = 'bustle_subscribers'
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ module Bustle::Storage::ActiveRecord
2
+ class Subscription < ::ActiveRecord::Base
3
+ self.table_name = 'bustle_subscriptions'
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ require 'active_record'
2
+ require 'bustle/storage/active_record/activity'
3
+ require 'bustle/storage/active_record/publisher'
4
+ require 'bustle/storage/active_record/subscriber'
5
+ require 'bustle/storage/active_record/subscription'
@@ -0,0 +1,7 @@
1
+ module Bustle
2
+ class Subscribers
3
+ RESOURCE_NAME = 'Bustle::Subscriber'
4
+
5
+ include Concern::ResourceCollection
6
+ end
7
+ 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
@@ -1,3 +1,3 @@
1
1
  module Bustle
2
- VERSION = "0.0.1"
2
+ VERSION = "0.1.0"
3
3
  end
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,9 @@
1
+ require_relative '../../spec_helper'
2
+
3
+ module Bustle
4
+ describe 'Activity' do
5
+ let(:described_class) { Activity }
6
+
7
+ it_behaves_like 'model'
8
+ end
9
+ 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,11 @@
1
+ require_relative '../../spec_helper'
2
+
3
+ module Bustle
4
+ describe 'Subscriber' do
5
+ let(:described_class) { Subscriber }
6
+
7
+ include_context 'app_resources'
8
+ it_behaves_like 'model'
9
+ it_behaves_like 'has_activities'
10
+ end
11
+ end
@@ -0,0 +1,9 @@
1
+ require_relative '../../spec_helper'
2
+
3
+ module Bustle
4
+ describe 'Subscription' do
5
+ let(:described_class) { Subscription }
6
+
7
+ it_behaves_like 'model'
8
+ end
9
+ end
@@ -0,0 +1,8 @@
1
+ require_relative '../spec_helper'
2
+
3
+ module Bustle
4
+ describe Publishers do
5
+ include_context 'app_resources'
6
+ it_behaves_like 'resource_collection'
7
+ end
8
+ 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,8 @@
1
+ require_relative '../spec_helper'
2
+
3
+ module Bustle
4
+ describe Subscribers do
5
+ include_context 'app_resources'
6
+ it_behaves_like 'resource_collection'
7
+ end
8
+ 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.1
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-10 00:00:00.000000000 Z
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: minitest-colorize
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
- - lib/specs/spec_helper.rb
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: 3392583862411400704
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: 3392583862411400704
246
+ hash: 1631565603439208655
101
247
  requirements: []
102
248
  rubyforge_project:
103
249
  rubygems_version: 1.8.24
@@ -1,8 +0,0 @@
1
- require 'simplecov'
2
- SimpleCov.start
3
-
4
- require 'minitest-colorize'
5
- require 'minitest/autorun'
6
- require 'minitest/spec'
7
-
8
- require File.expand_path('../../lib/bustle', __FILE__)