active_pubsub 0.0.6 → 0.0.7

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: e22f01f9cf4b2f63a384b21e4aa28b9623ef20b0
4
- data.tar.gz: 2376b5d7171510a5087cfb021c50d105f0d480f3
3
+ metadata.gz: 4c4e0a20248f5de90d821e8e07db538c76dd121c
4
+ data.tar.gz: c3e9ffbf7d9faef44bf37393f38b39cfd3bfd806
5
5
  SHA512:
6
- metadata.gz: f4fd96f0626db35b97853c803f8af0ace6746cce0910159187f18b504ddb3e6dbd1462d8f7ab9468f5d07c3090b0056aa24e61c52f8b0600a2ee3e72a0d1a7df
7
- data.tar.gz: bb359a492946e1c60ca0d6cac8c50498c03573a3f03bc712bcfead4792915b8395fc51c0f5670a5994142439fc37bc6f258bd1c19f1b54c19688169eec541cf9
6
+ metadata.gz: 56af865a31f3bc9939f079c800191f9976a04d5039556d3e7721d74a9a59cda5e9d9560f439ec91fbaec689e0c5ae2f2ad09f5002ccf1cc529c525e009d560fb
7
+ data.tar.gz: 0604dcb58eec116f71ae5c964d87f5a455c5aae35a2d1708ab5fdeae84aa36028efc45f2557dc2910e10dddc4a5547756f5b30be0aae13e8fe049d210ad63f4b
data/README.md CHANGED
@@ -39,6 +39,24 @@ class PostSubscriber < ::ActivePubsub::Subscriber
39
39
  end
40
40
  ```
41
41
 
42
+ ### Subscribe to individual attribute changes
43
+
44
+ ``` ruby
45
+ class PostSubscriber < ::ActivePubsub::Subscriber
46
+ include ::ActivePubsub::SubscribeToChanges
47
+
48
+ # Note: Do NOT define updated as an event, as on_change
49
+ # uses updated event so latter event will override former
50
+ observes "cms"
51
+ as "aggregator"
52
+
53
+ on_change :title do |new_value, old_value|
54
+ puts record.inspect
55
+ puts new_value
56
+ end
57
+ end
58
+ ```
59
+
42
60
  ## Publishing events
43
61
 
44
62
  Just include ::ActivePubsub::Publishable module into an active record class whose events you want to publish.
@@ -37,8 +37,12 @@ module ActivePubsub
37
37
  end
38
38
 
39
39
  def self.start_subscribers
40
- puts "Starting subscriber"
40
+
41
41
  ::ActivePubsub::Subscriber.subclasses.each do |subscriber|
42
+ next if subscriber.started?
43
+
44
+ puts "Starting #{subscriber.name}"
45
+
42
46
  subscriber.bind_subscriptions!
43
47
  subscriber.print_subscriptions!
44
48
  end
@@ -55,6 +59,7 @@ require "active_pubsub/config"
55
59
  require "active_pubsub/event"
56
60
  require "active_pubsub/publisher"
57
61
  require "active_pubsub/publishable"
58
- require "active_pubsub/subscriber"
59
62
  require "active_pubsub/publish_with_serializer"
63
+ require "active_pubsub/subscriber"
64
+ require "active_pubsub/subscribe_to_changes"
60
65
  require 'active_pubsub/railtie' if defined?(Rails)
@@ -9,13 +9,16 @@ module ActivePubsub
9
9
  end
10
10
 
11
11
  def serialized_resource
12
- serialized_resource_attributes.merge!(:changes => previous_changes) if previous_changes
12
+ serialized_resource_attributes.merge!(:changes => previous_changes) if previous_changes
13
13
 
14
14
  ::Marshal.dump(serialized_resource_attributes)
15
15
  end
16
16
 
17
17
  def serialized_resource_attributes
18
- @serialized_resource_attributes ||= self.class.publish_serializer.new(self).attributes
18
+ @serialized_resource_attributes ||= self.class
19
+ .publish_serializer.new(self)
20
+ .attributes
21
+ .symbolize_keys!
19
22
  end
20
23
 
21
24
  module ClassMethods
@@ -21,6 +21,7 @@ module ActivePubsub
21
21
  def attributes_hash
22
22
  hash = self.as_json
23
23
  hash.merge!(:changes => previous_changes) if previous_changes && hash
24
+ hash.symbolize_keys! if hash
24
25
  hash
25
26
  end
26
27
 
@@ -0,0 +1,26 @@
1
+ module ActivePubsub
2
+ module SubscribeToChanges
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ class_attribute :attributes_to_watch_for_changes
7
+ self.attributes_to_watch_for_changes = {}
8
+
9
+ on :updated do |changed_record|
10
+ attributes_to_watch_for_changes.each_pair do |field, block|
11
+ if changed_record[:changes].with_indifferent_access.has_key?(field)
12
+ old_value = changed_record[:changes][field][0]
13
+ new_value = changed_record[:changes][field][1]
14
+ instance_exec(new_value, old_value, &block)
15
+ end
16
+ end
17
+ end
18
+ end
19
+
20
+ module ClassMethods
21
+ def on_change(attribute_name, &block)
22
+ attributes_to_watch_for_changes[attribute_name] = block
23
+ end
24
+ end
25
+ end
26
+ end
@@ -8,11 +8,16 @@ module ActivePubsub
8
8
  class_attribute :exchange_name
9
9
  class_attribute :connection
10
10
  class_attribute :local_service_namespace
11
+ class_attribute :started
11
12
 
12
- self.events = {}
13
13
  self.connection = ::ActivePubsub::Connection.new
14
+ self.started = false
14
15
 
15
16
  ### Class Methods ###
17
+ def self.as(service_namespace)
18
+ self.local_service_namespace = service_namespace
19
+ end
20
+
16
21
  def self.channel
17
22
  connection.channel
18
23
  end
@@ -21,8 +26,8 @@ module ActivePubsub
21
26
  channel.topic(exchange_name, :auto_delete => true)
22
27
  end
23
28
 
24
- def self.as(service_namespace)
25
- self.local_service_namespace = service_namespace
29
+ def self.inherited(klass)
30
+ klass.events = {}
26
31
  end
27
32
 
28
33
  def self.on(event_name, &block)
@@ -30,6 +35,8 @@ module ActivePubsub
30
35
  end
31
36
 
32
37
  def self.bind_subscriptions!
38
+ return if started?
39
+
33
40
  events.each_pair do |event_name, block|
34
41
  channel.queue(queue_for_event(event_name.to_s))
35
42
  .bind(exchange, :routing_key => routing_key_for_event(event_name))
@@ -41,6 +48,8 @@ module ActivePubsub
41
48
  subscriber_instance.instance_exec(deserialized_record, &block)
42
49
  end
43
50
  end
51
+
52
+ self.started = true
44
53
  end
45
54
 
46
55
  def self.deserialize_event(event)
@@ -74,6 +83,10 @@ module ActivePubsub
74
83
  puts message
75
84
  end
76
85
 
86
+ def self.started?
87
+ self.started
88
+ end
89
+
77
90
  ### Instance Methods ###
78
91
  attr_accessor :record
79
92
 
@@ -1,3 +1,3 @@
1
1
  module ActivePubsub
2
- VERSION = "0.0.6"
2
+ VERSION = "0.0.7"
3
3
  end
@@ -24,7 +24,7 @@ describe ::ActivePubsub::Publishable do
24
24
  let(:created_record) { ::Fake::Blog::Post.create!(:title => "Post about nothing") }
25
25
 
26
26
  it "should merge previous changes" do
27
- expect(created_record.attributes_hash).to have_key("title")
27
+ expect(created_record.attributes_hash).to have_key(:title)
28
28
  end
29
29
  end
30
30
  end
@@ -0,0 +1,42 @@
1
+ require 'spec_helper'
2
+ require 'support/subscribers/author_subscriber'
3
+
4
+ describe ::ActivePubsub::SubscribeToChanges do
5
+ before do
6
+ ::ActivePubsub.start_subscribers
7
+ end
8
+
9
+ subject { ::AuthorSubscriber }
10
+
11
+ let(:fake_author) { ::Fake::Blog::Author.create!(:first_name => "Bill", :last_name => "Lumberg") }
12
+ let(:fake_post) { ::Fake::Blog::Post.create!(:title => "Fake post by #{fake_author.first_name}", :author_id => fake_author.id) }
13
+
14
+ its(:attributes_to_watch_for_changes) {
15
+ should have_key(:first_name)
16
+ }
17
+
18
+ describe "#on_change" do
19
+ context "when author last name is changed" do
20
+ it "does not update titles" do
21
+ fake_post
22
+
23
+ fake_author.last_name = "Bolton"
24
+ fake_author.save
25
+
26
+ fake_author.posts.last.title.should eq "Fake post by Bill"
27
+ end
28
+ end
29
+
30
+ context "when author first name is changed" do
31
+ # ghetto, but since its async we need to sleep to see update
32
+ it "updates all titles with new author name" do
33
+ fake_post
34
+
35
+ fake_author.first_name = "Michael"
36
+ fake_author.save
37
+ sleep(0.5)
38
+ fake_author.posts.last.title.should eq "Fake post by Michael"
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,51 @@
1
+ require 'spec_helper'
2
+
3
+ describe ActivePubsub::Subscriber do
4
+
5
+ subject { ::PostSubscriber }
6
+
7
+ its(:events) { should have_key(:created) }
8
+ its(:events) { should have_key(:destroyed) }
9
+ its(:events) { should have_key(:updated) }
10
+ its(:exchange_name) { should eq "test.post" }
11
+ its(:local_service_namespace) { should eq "test" }
12
+ its(:started) { should be true }
13
+ its(:connection) { should be_a(ActivePubsub::Connection) }
14
+
15
+ describe ".on" do
16
+ context ":created" do
17
+ let!(:fake_author) { ::Fake::Blog::Author.create!(:first_name => "Samir", :last_name => "Nyininejad") }
18
+ let!(:fake_post) { ::Fake::Blog::Post.create!(:title => "myblogpost", :author_id => fake_author.id) }
19
+
20
+ it "should increment post_count" do
21
+ sleep(0.1)
22
+ ::Fake::Blog::AuthorPostReport.find_by(:author_id => fake_author.id).post_count.should eq 1
23
+ end
24
+ end
25
+
26
+ context ":destroyed" do
27
+ let!(:fake_author) { ::Fake::Blog::Author.create!(:first_name => "Samir", :last_name => "Nyininejad") }
28
+ let!(:fake_post) { ::Fake::Blog::Post.create!(:title => "myblogpost", :author_id => fake_author.id) }
29
+
30
+ it "should destroy author post report" do
31
+ fake_post.destroy
32
+ sleep(0.1)
33
+ ::Fake::Blog::AuthorPostReport.where(:author_id => fake_author.id).first.should be_nil
34
+ end
35
+ end
36
+
37
+ context ":updated" do
38
+ let!(:fake_author) { ::Fake::Blog::Author.create!(:first_name => "Samir", :last_name => "Nyininejad") }
39
+ let!(:fake_post) { ::Fake::Blog::Post.create!(:title => "myblogpost", :author_id => fake_author.id) }
40
+
41
+ it "should update author last name with author_id" do
42
+ fake_post.title = "Asdfg"
43
+ fake_post.save
44
+
45
+ sleep(0.1)
46
+
47
+ fake_author.reload.last_name.should eq "author_#{fake_author.id}"
48
+ end
49
+ end
50
+ end
51
+ end
@@ -9,6 +9,9 @@ SimpleCov.start do
9
9
  end
10
10
 
11
11
  RSpec.configure do |config|
12
+ config.before(:suite) do
13
+ ::ActivePubsub.start_subscribers
14
+ end
12
15
  end
13
16
 
14
17
  Bundler.require(:default, :development, :test)
@@ -14,16 +14,20 @@ ActiveRecord::Schema.define(:version => 1) do
14
14
  t.string :body
15
15
  t.string :title
16
16
  t.string :slug
17
- t.integer :user_id
17
+ t.integer :author_id
18
18
 
19
19
  t.timestamps
20
20
  end
21
21
 
22
+ create_table :author_post_reports do |t|
23
+ t.integer :author_id
24
+ t.integer :post_count
25
+ end
26
+
22
27
  create_table :authors do |t|
23
28
  t.string :first_name
24
29
  t.string :last_name
25
30
  t.string :slug
26
- t.integer :user_id
27
31
 
28
32
  t.timestamps
29
33
  end
@@ -9,6 +9,8 @@ module Fake
9
9
  serialize_publish_with ::AuthorSerializer
10
10
 
11
11
  publish_as "test"
12
+
13
+ has_many :posts, :class_name => "Fake::Blog::Post"
12
14
  end
13
15
  end
14
16
  end
@@ -0,0 +1,9 @@
1
+ module Fake
2
+ module Blog
3
+ class AuthorPostReport < ::ActiveRecord::Base
4
+ # after_initialize do
5
+ # self[:post_count] ||= 0
6
+ # end
7
+ end
8
+ end
9
+ end
@@ -5,6 +5,8 @@ module Fake
5
5
  include ::ActivePubsub::Publishable
6
6
 
7
7
  publish_as "test"
8
+
9
+ belongs_to :author, :class_name => "Fake::Blog::Author"
8
10
  end
9
11
  end
10
12
  end
@@ -0,0 +1,16 @@
1
+ class AuthorSubscriber < ::ActivePubsub::Subscriber
2
+ include ::ActivePubsub::SubscribeToChanges
3
+
4
+ observes "test.author"
5
+ as "test"
6
+
7
+ on_change :first_name do |new_value, old_value|
8
+ author.posts.update_all(:title => "Fake post by #{new_value}")
9
+ end
10
+
11
+ private
12
+
13
+ def author
14
+ @author ||= ::Fake::Blog::Author.find(record[:id])
15
+ end
16
+ end
@@ -0,0 +1,30 @@
1
+ class PostSubscriber < ::ActivePubsub::Subscriber
2
+
3
+ observes "test.post"
4
+ as "test"
5
+
6
+ # Pointless as examples, but being used for integration testing
7
+
8
+ on :created do
9
+ author_post_report.increment!(:post_count) if record.has_key?(:author_id)
10
+ end
11
+
12
+ on :updated do
13
+ author.last_name = "author_#{record[:author_id]}"
14
+ author.save
15
+ end
16
+
17
+ on :destroyed do
18
+ author_post_report.destroy
19
+ end
20
+
21
+ private
22
+
23
+ def author
24
+ @author ||= ::Fake::Blog::Author.find(record[:author_id])
25
+ end
26
+
27
+ def author_post_report
28
+ @author_post_report ||= ::Fake::Blog::AuthorPostReport.where(:author_id => record[:author_id]).first_or_create
29
+ end
30
+ end
Binary file
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: active_pubsub
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.6
4
+ version: 0.0.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jason Ayre
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-10-20 00:00:00.000000000 Z
11
+ date: 2014-10-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: active_attr
@@ -318,6 +318,7 @@ files:
318
318
  - lib/active_pubsub/publishable.rb
319
319
  - lib/active_pubsub/publisher.rb
320
320
  - lib/active_pubsub/railtie.rb
321
+ - lib/active_pubsub/subscribe_to_changes.rb
321
322
  - lib/active_pubsub/subscriber.rb
322
323
  - lib/active_pubsub/version.rb
323
324
  - spec/active_pubsub/config_spec.rb
@@ -325,13 +326,16 @@ files:
325
326
  - spec/active_pubsub/publish_with_serializer_spec.rb
326
327
  - spec/active_pubsub/publishable_spec.rb
327
328
  - spec/active_pubsub/publisher_spec.rb
329
+ - spec/active_pubsub/subscribe_to_changes_spec.rb
330
+ - spec/active_pubsub/subscriber_spec.rb
328
331
  - spec/spec_helper.rb
329
332
  - spec/support/db/setup.rb
330
- - spec/support/models.rb
331
333
  - spec/support/models/author.rb
334
+ - spec/support/models/author_post_report.rb
332
335
  - spec/support/models/post.rb
333
- - spec/support/serializers.rb
334
336
  - spec/support/serializers/author_serializer.rb
337
+ - spec/support/subscribers/author_subscriber.rb
338
+ - spec/support/subscribers/post_subscriber.rb
335
339
  - spec/test.db
336
340
  homepage: https://github.com/jasonayre/active_pubsub
337
341
  licenses:
@@ -364,11 +368,14 @@ test_files:
364
368
  - spec/active_pubsub/publish_with_serializer_spec.rb
365
369
  - spec/active_pubsub/publishable_spec.rb
366
370
  - spec/active_pubsub/publisher_spec.rb
371
+ - spec/active_pubsub/subscribe_to_changes_spec.rb
372
+ - spec/active_pubsub/subscriber_spec.rb
367
373
  - spec/spec_helper.rb
368
374
  - spec/support/db/setup.rb
369
- - spec/support/models.rb
370
375
  - spec/support/models/author.rb
376
+ - spec/support/models/author_post_report.rb
371
377
  - spec/support/models/post.rb
372
- - spec/support/serializers.rb
373
378
  - spec/support/serializers/author_serializer.rb
379
+ - spec/support/subscribers/author_subscriber.rb
380
+ - spec/support/subscribers/post_subscriber.rb
374
381
  - spec/test.db
@@ -1 +0,0 @@
1
- require 'support/models/post'
@@ -1 +0,0 @@
1
- require 'support/serializers/author_serializer'