active_pubsub 0.0.6 → 0.0.7

Sign up to get free protection for your applications and to get access to all the features.
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'