sub_pub 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,3 @@
1
+ *.swp
2
+ *.sqlite3
3
+
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format progress
data/Gemfile ADDED
@@ -0,0 +1,11 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in pub_sub.gemspec
4
+ gemspec
5
+
6
+ gem 'with_model'
7
+ gem 'rspec'
8
+ gem 'activerecord'
9
+ gem 'rails'
10
+ gem 'sqlite3'
11
+ gem 'pivotal_git_scripts'
data/Gemfile.lock ADDED
@@ -0,0 +1,111 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ sub_pub (0.0.3)
5
+
6
+ GEM
7
+ remote: https://rubygems.org/
8
+ specs:
9
+ actionmailer (3.2.9)
10
+ actionpack (= 3.2.9)
11
+ mail (~> 2.4.4)
12
+ actionpack (3.2.9)
13
+ activemodel (= 3.2.9)
14
+ activesupport (= 3.2.9)
15
+ builder (~> 3.0.0)
16
+ erubis (~> 2.7.0)
17
+ journey (~> 1.0.4)
18
+ rack (~> 1.4.0)
19
+ rack-cache (~> 1.2)
20
+ rack-test (~> 0.6.1)
21
+ sprockets (~> 2.2.1)
22
+ activemodel (3.2.9)
23
+ activesupport (= 3.2.9)
24
+ builder (~> 3.0.0)
25
+ activerecord (3.2.9)
26
+ activemodel (= 3.2.9)
27
+ activesupport (= 3.2.9)
28
+ arel (~> 3.0.2)
29
+ tzinfo (~> 0.3.29)
30
+ activeresource (3.2.9)
31
+ activemodel (= 3.2.9)
32
+ activesupport (= 3.2.9)
33
+ activesupport (3.2.9)
34
+ i18n (~> 0.6)
35
+ multi_json (~> 1.0)
36
+ arel (3.0.2)
37
+ builder (3.0.4)
38
+ diff-lcs (1.1.3)
39
+ erubis (2.7.0)
40
+ hike (1.2.1)
41
+ i18n (0.6.1)
42
+ journey (1.0.4)
43
+ json (1.7.5)
44
+ mail (2.4.4)
45
+ i18n (>= 0.4.0)
46
+ mime-types (~> 1.16)
47
+ treetop (~> 1.4.8)
48
+ mime-types (1.19)
49
+ multi_json (1.5.0)
50
+ pivotal_git_scripts (1.1.4)
51
+ polyglot (0.3.3)
52
+ rack (1.4.1)
53
+ rack-cache (1.2)
54
+ rack (>= 0.4)
55
+ rack-ssl (1.3.2)
56
+ rack
57
+ rack-test (0.6.2)
58
+ rack (>= 1.0)
59
+ rails (3.2.9)
60
+ actionmailer (= 3.2.9)
61
+ actionpack (= 3.2.9)
62
+ activerecord (= 3.2.9)
63
+ activeresource (= 3.2.9)
64
+ activesupport (= 3.2.9)
65
+ bundler (~> 1.0)
66
+ railties (= 3.2.9)
67
+ railties (3.2.9)
68
+ actionpack (= 3.2.9)
69
+ activesupport (= 3.2.9)
70
+ rack-ssl (~> 1.3.2)
71
+ rake (>= 0.8.7)
72
+ rdoc (~> 3.4)
73
+ thor (>= 0.14.6, < 2.0)
74
+ rake (10.0.3)
75
+ rdoc (3.12)
76
+ json (~> 1.4)
77
+ rspec (2.12.0)
78
+ rspec-core (~> 2.12.0)
79
+ rspec-expectations (~> 2.12.0)
80
+ rspec-mocks (~> 2.12.0)
81
+ rspec-core (2.12.1)
82
+ rspec-expectations (2.12.0)
83
+ diff-lcs (~> 1.1.3)
84
+ rspec-mocks (2.12.0)
85
+ sprockets (2.2.2)
86
+ hike (~> 1.2)
87
+ multi_json (~> 1.0)
88
+ rack (~> 1.0)
89
+ tilt (~> 1.1, != 1.3.0)
90
+ sqlite3 (1.3.6)
91
+ thor (0.16.0)
92
+ tilt (1.3.3)
93
+ treetop (1.4.12)
94
+ polyglot
95
+ polyglot (>= 0.3.1)
96
+ tzinfo (0.3.35)
97
+ with_model (0.3.1)
98
+ activerecord (>= 2.3.5, < 4.0.0)
99
+ rspec (~> 2.11)
100
+
101
+ PLATFORMS
102
+ ruby
103
+
104
+ DEPENDENCIES
105
+ activerecord
106
+ pivotal_git_scripts
107
+ rails
108
+ rspec
109
+ sqlite3
110
+ sub_pub!
111
+ with_model
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 TODO: Write your name
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,122 @@
1
+ # SubPub
2
+
3
+ SubPub is a thin wrapper around ActiveSupport::Notifications, which provides an implementation of the Publish/Subscribe pattern.
4
+
5
+ http://api.rubyonrails.org/classes/ActiveSupport/Notifications.html
6
+
7
+ The goal was to be able to create more loosly coupled models by pulling side effects of our models into their own class. We wanted this behavior for Active Record classes as well as non-Active Record classes.
8
+
9
+ The result was a library that was:
10
+ * easy to test
11
+ * easy to disable (when using the console, during unit tests, etc.)
12
+ * easy to understand (b/c of AS::Notifications syncronous publishing queue)
13
+
14
+ ## Installation
15
+
16
+ Add this line to your application's Gemfile:
17
+
18
+ gem 'sub_pub'
19
+
20
+ And then execute:
21
+
22
+ $ bundle
23
+
24
+ Or install it yourself as:
25
+
26
+ $ gem install sub_pub
27
+
28
+ ## Usage
29
+
30
+ ### Configuration
31
+
32
+ SubPub.disable # disables publishing
33
+ SubPub.enable # enables publishing
34
+
35
+ By default, SubPub looks for subscribers in app/models/pub_sub/*
36
+
37
+
38
+ ### Within a plain ruby project
39
+
40
+ SubPub::Subscriber => provides an interface to subscribe to a topic
41
+
42
+ #
43
+ # Subscribe to the 'user_created' topic.
44
+ #
45
+ class SendWelcomeEmailToUser < SubPub::Subscriber
46
+ subscribe_to("user_created")
47
+
48
+ def on_publish
49
+ write_user_to_file(options[:user])
50
+ end
51
+
52
+ private
53
+
54
+ def write_user_to_file(user)
55
+ File.open("/tmp/users.txt", "w+") do |file|
56
+ file << user.name
57
+ end
58
+ end
59
+ end
60
+
61
+
62
+ #
63
+ # Publish a message to the 'user_created' topic
64
+ #
65
+ SubPub.publish("user_created", user:
66
+ User.new(name: 'John Doe')
67
+ )
68
+
69
+
70
+ ### Within a Rails project
71
+
72
+ SubPub::ActiveRecord::Subscriber => provide an interface to subscribe to an ActiveRecord callback topic
73
+
74
+ #
75
+ # Subscribe to the after_create callback of User
76
+ #
77
+ # currently, SubPub loads all subscribers in app/models/pub_sub/*
78
+ #
79
+ class SendWelcomeEmailToUser < SubPub::ActiveRecord::Subscriber
80
+ subscribe_to(User, 'after_create')
81
+
82
+ def on_publish
83
+ ApplicationMailer.send_welcome_email(record)
84
+ end
85
+ end
86
+ => SubPub.subscribe("active_record::user::after_create")
87
+
88
+
89
+ #
90
+ # inside your application (controller, model, etc)
91
+ #
92
+ User.create(name: 'John Doe')
93
+ => SubPub.publish("active_record::user::after_create")
94
+
95
+ ## Backlog / To Do
96
+
97
+ http://www.pivotaltracker.com/projects/705655
98
+
99
+ Feel free to take a look.
100
+
101
+ ## Contributing
102
+
103
+ 1. Fork it
104
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
105
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
106
+ 4. Push to the branch (`git push origin my-new-feature`)
107
+ 5. Create new Pull Request
108
+
109
+ # Contributors
110
+
111
+ * Brent Wheeldon
112
+ * Cathy O'Connell
113
+ * Nick Monje
114
+ * Evan Goodberry
115
+ * Ben Moss
116
+ * Chien Kuo
117
+ * Edwin Chong
118
+ * Adam Berlin
119
+ * Rasheed Abdul-Aziz
120
+ * Ryan McGarvey
121
+ * Geoffrey Ducharme
122
+ * Alex Kramer
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
data/db/.gitkeep ADDED
File without changes
@@ -0,0 +1,38 @@
1
+ module SubPub
2
+ class ActiveRecordExtensions < Rails::Railtie
3
+ initializer "pub sub configuration of active record extensions" do
4
+ class ::ActiveRecord::Base
5
+ after_create :notify_of_after_create
6
+
7
+ private
8
+
9
+ def notify_of_after_create
10
+ notify_pub_sub_of_active_record_callback('after_create')
11
+ end
12
+
13
+ def notify_pub_sub_of_active_record_callback(callback)
14
+ message = "active_record::#{self.class.to_s.underscore}::#{callback}"
15
+ SubPub.publish(message, record: self)
16
+ end
17
+ end
18
+
19
+ config.after_initialize do
20
+ Dir[
21
+ File.expand_path("app/models/pub_sub/*.rb", Rails.root)
22
+ ].each { |file| require file }
23
+ end
24
+ end
25
+ end
26
+
27
+ module ActiveRecord
28
+ class Subscriber < SubPub::Subscriber
29
+ def self.subscribe_to(class_instance, callback)
30
+ super("active_record::#{class_instance.to_s.underscore}::#{callback}")
31
+ end
32
+
33
+ def record
34
+ options[:record]
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,46 @@
1
+ require "singleton"
2
+
3
+ module SubPub
4
+ class Register
5
+ include Singleton
6
+
7
+ attr_accessor :enabled
8
+
9
+ def initialize
10
+ @enabled = true
11
+ super
12
+ end
13
+
14
+ class << self
15
+ def enable
16
+ instance.enabled = true
17
+ end
18
+
19
+ def disable
20
+ instance.enabled = false
21
+ end
22
+
23
+ def enabled?
24
+ if instance.enabled.nil?
25
+ instance.enabled = true
26
+ end
27
+
28
+ instance.enabled
29
+ end
30
+
31
+ def disabled?
32
+ !instance.enabled
33
+ end
34
+
35
+ def publish(*args, &block)
36
+ return if disabled?
37
+
38
+ ActiveSupport::Notifications.publish(*args, &block)
39
+ end
40
+
41
+ def subscribe(*args, &block)
42
+ ActiveSupport::Notifications.subscribe(*args, &block)
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,34 @@
1
+ module SubPub
2
+ class Subscriber
3
+ attr_reader :options
4
+ alias :payload :options
5
+
6
+ def initialize(options)
7
+ @options = options
8
+ end
9
+
10
+ def self.subscribe_to(topic_name)
11
+ klass = self
12
+
13
+ @subscription = SubPub.subscribe(topic_name) do |topic, options|
14
+ publish(klass.new(options))
15
+ end
16
+ end
17
+
18
+ def self.publish(subscription)
19
+ subscription.on_publish
20
+ end
21
+
22
+ def self.subscription
23
+ @subscription
24
+ end
25
+
26
+ def self.topic
27
+ @subscription.instance_variable_get("@pattern")
28
+ end
29
+
30
+ def on_publish
31
+ raise "Please define an on_publish method for #{self.class.name}"
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,3 @@
1
+ module SubPub
2
+ VERSION = "0.0.3"
3
+ end
data/lib/sub_pub.rb ADDED
@@ -0,0 +1,39 @@
1
+ module SubPub
2
+ class << self
3
+ def enable
4
+ Register.enable
5
+ end
6
+
7
+ def disable
8
+ Register.disable
9
+ end
10
+
11
+ def enabled?
12
+ Register.enabled?
13
+ end
14
+
15
+ def disabled?
16
+ Register.disabled?
17
+ end
18
+
19
+ #
20
+ # Standardize on Pub/Sub naming
21
+ #
22
+ def publish(*args, &block)
23
+ Register.publish(*args, &block)
24
+ end
25
+
26
+ def subscribe(*args, &block)
27
+ Register.subscribe(*args, &block)
28
+ end
29
+ end
30
+ end
31
+
32
+ require "sub_pub/version"
33
+ require "sub_pub/subscriber"
34
+ require "sub_pub/register"
35
+
36
+ require 'rails'
37
+ require 'active_record'
38
+
39
+ require_relative "sub_pub/active_record_extensions"
Binary file
@@ -0,0 +1,66 @@
1
+ require 'spec_helper'
2
+
3
+ describe SubPub do
4
+ describe "initial state" do
5
+ it "defaults enabled to true" do
6
+ SubPub::Register.instance.enabled = nil
7
+ SubPub.enabled?.should be_true
8
+ end
9
+ end
10
+
11
+ describe "#enable" do
12
+ it "enables SubPub" do
13
+ SubPub.disable
14
+ SubPub.enable
15
+ SubPub.enabled?.should be_true
16
+ end
17
+ end
18
+
19
+ describe "#disable" do
20
+ it "disables SubPub" do
21
+ SubPub.disable
22
+ SubPub.enabled?.should be_false
23
+ end
24
+ end
25
+
26
+ describe "#publish" do
27
+ context "when disabled" do
28
+ before { SubPub.disable }
29
+
30
+ it "does not publish" do
31
+ ActiveSupport::Notifications.should_receive(:publish).never
32
+ SubPub.publish
33
+ end
34
+ end
35
+ end
36
+
37
+ describe "active record configuration" do
38
+ with_model :FakeActiveRecordUser do
39
+ table do |t|
40
+ t.string :title
41
+ end
42
+ end
43
+
44
+ with_model :FakeActiveRecordResult do
45
+ table do |t|
46
+ t.string :title
47
+ end
48
+ end
49
+
50
+ before do
51
+ class FakeActiveRecordUserSubscriber < SubPub::ActiveRecord::Subscriber
52
+ subscribe_to(FakeActiveRecordUser, 'after_create')
53
+
54
+ def on_publish
55
+ FakeActiveRecordResult.create
56
+ end
57
+ end
58
+ end
59
+
60
+ it "successfully calls through to the subscriber" do
61
+ FakeActiveRecordResult.all.size.should == 0
62
+ FakeActiveRecordUser.create
63
+ FakeActiveRecordResult.all.size.should == 1
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,50 @@
1
+ # This file was generated by the `rspec --init` command. Conventionally, all
2
+ # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
3
+ # Require this file using `require "spec_helper"` to ensure that it is only
4
+ # loaded once.
5
+ #
6
+ # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
7
+ RSpec.configure do |config|
8
+ config.treat_symbols_as_metadata_keys_with_true_values = true
9
+ config.run_all_when_everything_filtered = true
10
+ config.filter_run :focus
11
+
12
+ # Run specs in random order to surface order dependencies. If you find an
13
+ # order dependency and want to debug it, you can fix the order by providing
14
+ # the seed, which is printed after each run.
15
+ # --seed 1234
16
+ config.order = 'random'
17
+
18
+ #
19
+ # Load the environment
20
+ #
21
+ require 'bundler'
22
+ Bundler.require
23
+
24
+ #
25
+ # Rails loads this during its setup
26
+ #
27
+ SubPub::ActiveRecordExtensions.run_initializers
28
+
29
+ #
30
+ # Test database
31
+ #
32
+ ActiveRecord::Base.establish_connection(
33
+ adapter: 'sqlite3',
34
+ database: 'db/test.sqlite3',
35
+ pool: 5,
36
+ timeout: 5000
37
+ )
38
+
39
+ #
40
+ # Use with_model
41
+ #
42
+ config.extend WithModel
43
+
44
+ #
45
+ # Ensure pubsub is enabled
46
+ #
47
+ config.before do
48
+ SubPub.enable
49
+ end
50
+ end
@@ -0,0 +1,55 @@
1
+ require 'spec_helper'
2
+
3
+ describe SubPub::Subscriber do
4
+ class FooBar < SubPub::Subscriber
5
+ end
6
+
7
+ describe ".topic" do
8
+ it "sets up a subscription" do
9
+ FooBar.class_eval do
10
+ subscribe_to("foo")
11
+ end
12
+
13
+ FooBar.topic.should == 'foo'
14
+ end
15
+ end
16
+
17
+ describe ".subscription" do
18
+ it "should be the result of the subscribe" do
19
+ subscription = stub
20
+
21
+ SubPub.should_receive(:subscribe).with('topic-name') { subscription }
22
+
23
+ FooBar.class_eval do
24
+ subscribe_to("topic-name")
25
+ end
26
+
27
+ FooBar.subscription.should be(subscription)
28
+ end
29
+ end
30
+
31
+ describe ".publish" do
32
+ it "notifies the subscription of the publish event" do
33
+ subscription = double
34
+ subscription.should_receive(:on_publish)
35
+ FooBar.publish(subscription)
36
+ end
37
+ end
38
+
39
+ describe "#on_publish" do
40
+ it "warns developers that they need to implement the interface" do
41
+ expect {FooBar.new({}).on_publish}.to raise_error(/Please define/)
42
+ end
43
+ end
44
+
45
+ describe "payload" do
46
+ let(:payload) { double }
47
+ it "has a payload" do
48
+ FooBar.new(payload).payload.should == payload
49
+ end
50
+
51
+ it "has options" do
52
+ FooBar.new(payload).options.should == payload
53
+ end
54
+ end
55
+ end
data/sub_pub.gemspec ADDED
@@ -0,0 +1,19 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'sub_pub/version'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = "sub_pub"
8
+ gem.version = SubPub::VERSION
9
+ gem.authors = ["zephyr-dev@googlegroups.com"]
10
+ gem.email = ["zephyr-dev@googlegroups.com"]
11
+ gem.description = %q{In process publish/subscribe for Ruby}
12
+ gem.summary = %q{SubPub is a thin wrapper around ActiveSupport::Notifications, which provides an implementation of the Publish/Subscribe pattern.}
13
+ gem.homepage = ""
14
+
15
+ gem.files = `git ls-files`.split($/)
16
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
17
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
18
+ gem.require_paths = ["lib"]
19
+ end
metadata ADDED
@@ -0,0 +1,73 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: sub_pub
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.3
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - zephyr-dev@googlegroups.com
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-12-13 00:00:00.000000000 Z
13
+ dependencies: []
14
+ description: In process publish/subscribe for Ruby
15
+ email:
16
+ - zephyr-dev@googlegroups.com
17
+ executables: []
18
+ extensions: []
19
+ extra_rdoc_files: []
20
+ files:
21
+ - .gitignore
22
+ - .rspec
23
+ - Gemfile
24
+ - Gemfile.lock
25
+ - LICENSE.txt
26
+ - README.md
27
+ - Rakefile
28
+ - db/.gitkeep
29
+ - lib/sub_pub.rb
30
+ - lib/sub_pub/active_record_extensions.rb
31
+ - lib/sub_pub/register.rb
32
+ - lib/sub_pub/subscriber.rb
33
+ - lib/sub_pub/version.rb
34
+ - pkg/sub_pub-0.0.3.gem
35
+ - spec/integration/pub_sub_spec.rb
36
+ - spec/spec_helper.rb
37
+ - spec/unit/subscriber_spec.rb
38
+ - sub_pub.gemspec
39
+ homepage: ''
40
+ licenses: []
41
+ post_install_message:
42
+ rdoc_options: []
43
+ require_paths:
44
+ - lib
45
+ required_ruby_version: !ruby/object:Gem::Requirement
46
+ none: false
47
+ requirements:
48
+ - - ! '>='
49
+ - !ruby/object:Gem::Version
50
+ version: '0'
51
+ segments:
52
+ - 0
53
+ hash: -923795167419613194
54
+ required_rubygems_version: !ruby/object:Gem::Requirement
55
+ none: false
56
+ requirements:
57
+ - - ! '>='
58
+ - !ruby/object:Gem::Version
59
+ version: '0'
60
+ segments:
61
+ - 0
62
+ hash: -923795167419613194
63
+ requirements: []
64
+ rubyforge_project:
65
+ rubygems_version: 1.8.24
66
+ signing_key:
67
+ specification_version: 3
68
+ summary: SubPub is a thin wrapper around ActiveSupport::Notifications, which provides
69
+ an implementation of the Publish/Subscribe pattern.
70
+ test_files:
71
+ - spec/integration/pub_sub_spec.rb
72
+ - spec/spec_helper.rb
73
+ - spec/unit/subscriber_spec.rb