custom_active_record_observer 0.3.0 → 0.3.1

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: d517d3fde840c8135daaf6eb3c5399ae908feca7
4
- data.tar.gz: 65d5a41cf511bef2dc452a9f47e6a52740fba8c3
3
+ metadata.gz: 4af7438aff8a12f47958c45d93a0fa4da7efbcba
4
+ data.tar.gz: 00116ca29ecc84e69dac009c5313a265e0697578
5
5
  SHA512:
6
- metadata.gz: 58dbf2b12928f3b643a402317b305c2388b997dab1caf1ca2084fbd4b6235389f9ebbc4d81e3852763b5a33749aa0aa88c4ed5ca70a09b334370d8beb3bc4daa
7
- data.tar.gz: 480d372c5311324ba4fb3c65c1a29d439921b59188095906c8aa4d354d74ac02275bfc9522bc977955973253dfc0287bcbdc34d0fafe944ae2f2a5a0ee8e1f78
6
+ metadata.gz: d0718789103b5aaac520e90497227f4484877317f3351b37dba4104a2b45839847d502b818682829fca542e19dfcc5b1e94f9723cb8dd454e2f918f4f0d4679e
7
+ data.tar.gz: 5ad0514aa41b98a010e30f8c714f30cd0f01c12b5a520de5c76f3c6f390b43d440eb132d96685b51e81ae0bf31f9e963668a7e7bb6e9f09e9849447f9ef5d393
data/README.md CHANGED
@@ -1,25 +1,99 @@
1
+ [gem]: https://rubygems.org/gems/custom_active_record_observer
2
+ [travis]: https://travis-ci.org/OnApp/custom_active_record_observer
3
+
1
4
  # CustomActiveRecordObserver
2
5
 
3
- `custom_active_record_observer` is a DSL to track create/update/destroy operations based on AR callbacks.
6
+ [![Gem Version](https://badge.fury.io/rb/custom_active_record_observer.svg)][gem]
7
+ [![Build Status](https://travis-ci.org/OnApp/custom_active_record_observer.svg?branch=master)][travis]
8
+
9
+ A DSL to track create/update/destroy operations based on AR callbacks.
4
10
 
5
- Its a simple DSL similar to the code below:
11
+ ### Installation
12
+ Add the following code to your `Gemfile`
6
13
 
14
+ ```ruby
15
+ # Gemfile
16
+ gem 'custom_active_record_observer'
7
17
  ```
8
- CustomActiveRecordObserver.observe :EngagedUser, :CompanyOwner, handler: CustomerEventPublisher.new do
9
- on_create do |customer|
10
- Events::CustomerSignedUp.new(data: {customer_id: customer.id}
18
+ and run `bundle install`
19
+
20
+ Then add the observation rules (in `config/initializers/events_observation.rb`)
21
+ ```ruby
22
+ # config/initializers/events_observation.rb
23
+ CustomActiveRecordObserver.observe 'User' do
24
+ on_create do |user|
25
+ # do something
11
26
  end
27
+ end
28
+ ```
29
+
30
+ In this expample we track model `User`. If a new user is created, AR `:after_commit` callback is launched and the block in `on_create` executes.
12
31
 
13
- on_destroy do |customer|
14
- Events::CustomerSignedOut.new(data: {…})
32
+ We can track by real classes (which is not always possible, since that code is better to be placed in `config/initializers`). But passing strings or symbols is also allowed:
33
+ ```ruby
34
+ CustomActiveRecordObserver.observe User, :EngagedUser, 'CompanyOwner' { ... }
35
+ ```
36
+ Here we track three classes.
37
+
38
+ ### Usage
39
+ How do we use `custom_active_record_observer` in the wild? For instance, in this example we publish `UserRegisteredEvent` on user creation. (using `rails_event_store` gem). This is a pretty useful trade-off if there are no operations or aggregates introduced in the system architecture.
40
+
41
+ ```ruby
42
+ CustomActiveRecordObserver.observe 'User' do
43
+ on_create do |user|
44
+ Rails.application.config.event_store.publish_event(
45
+ UserRegisteredEvent.strict(data: { user_id: user.id }),
46
+ stream_name: "users-#{user.id}"
47
+ )
15
48
  end
49
+ end
50
+ ```
51
+
52
+ #### Usage with `handler`
53
+ `handler` might be passed into the observation. It is a simple object/class which has metho `call`. The result of `do..end` block is passed into that handler.
16
54
 
17
- on_change :group_id do |customer|
18
- Events::CustomerGroupChanged.new(data: {…})
55
+ ```ruby
56
+ handler = ->(event) do
57
+ Rails.application.config.event_store.publish_event(
58
+ event,
59
+ stream_name: "users-#{ event.data.fetch(:user_id) }"
60
+ )
61
+ end
62
+
63
+ CustomActiveRecordObserver.observe 'User', handler: handler do
64
+ on_create do |user|
65
+ UserRegisteredEvent.strict(data: { user_id: user.id })
66
+ end
67
+
68
+ on_add :user_group_id do |user|
69
+ UserAssignedToUserGroup.strict(data: {
70
+ user_id: user.id,
71
+ user_group_id: user.user_group_id
72
+ })
73
+ end
74
+
75
+ on_change :user_group_id do |user|
76
+ UserAssignedToAnotherUserGroupEvent.strict(data: {
77
+ user_id: user.id,
78
+ user_group_id: user.user_group_id
79
+ })
80
+ end
81
+
82
+ on_remove :user_group_id do |user|
83
+ UserUnassignedFromUserGroupEvent.strict(data: {
84
+ user_id: user.id,
85
+ user_group_id: user.previous_changes.fetch(:user_group_id).first
86
+ })
87
+ end
88
+
89
+ on_destroy do |user|
90
+ UserTerminatedEvent.strict(data: { user_id: user.id })
19
91
  end
20
-
21
92
  end
22
93
  ```
23
94
 
24
- which you can put into a single initializer file instead of scattering it around and having it in each AR model.
25
- That file is easier to remove in future. It incapsulates all the “inaccurate" dependencies.
95
+ ### Licence
96
+ See `LICENSE` file.
97
+
98
+ ## Contributing
99
+ Bug reports and pull requests are welcome on GitHub at https://github.com/OnApp/custom_active_record_observer.
@@ -6,6 +6,42 @@ module CustomActiveRecordObserver
6
6
  mattr_accessor :schema
7
7
  @@schema = Schema.new
8
8
 
9
+ # @param class_names [Symbols] - list of class names
10
+ # may be an object of Class, Symbol, or String
11
+ # (e.g. :User, 'User' or User)
12
+ # @example Usage without handler
13
+ # CustomActiveRecordObserver.observe 'User' do
14
+ # on_create do |user|
15
+ # Rails.application.config.event_store.publish_event(
16
+ # UserRegisteredEvent.strict(data: { user_id: user.id }),
17
+ # stream_name: "users-#{user.id}"
18
+ # )
19
+ # end
20
+ #
21
+ # on_destroy do |user|
22
+ # Rails.application.config.event_store.publish_event(
23
+ # UserTerminatedEvent.strict(data: { user_id: user.id }),
24
+ # stream_name: "users-#{user.id}"
25
+ # )
26
+ # end
27
+ # end
28
+ #
29
+ # @example Usage with handler
30
+ # handler = ->(event) {
31
+ # Rails.application.config.event_store.publish_event(
32
+ # event,
33
+ # stream_name: "users-#{event.data[:user_id]}"
34
+ # )
35
+ # }
36
+ # CustomActiveRecordObserver.observe 'User', handler: handler do
37
+ # on_create do |user|
38
+ # UserRegisteredEvent.strict(data: { user_id: user.id })
39
+ # end
40
+ #
41
+ # on_destroy do |user|
42
+ # UserTerminatedEvent.strict(data: { user_id: user.id })
43
+ # end
44
+ # end
9
45
  def self.observe(*class_names, handler: proc {}, &block)
10
46
  class_names.each do |class_name|
11
47
  DSL.new(block).actions_and_rules.each do |(action, rule)|
@@ -1,21 +1,50 @@
1
+ # @title CustomActiveRecordObserver::DSL
1
2
  module CustomActiveRecordObserver
3
+ # A main methods that are supposed to be used within CustomActiveRecordObserver
4
+ #
5
+ # @author OnApp Ltd.
2
6
  class DSL
7
+ attr_reader :actions_and_rules
8
+
9
+ # @attr_reader [Array] actions_and_rules the resulting list of applied rules
3
10
  def initialize(block)
4
- instance_exec(&block)
5
- end
11
+ @actions_and_rules = []
6
12
 
7
- def actions_and_rules
8
- @actions_and_rules ||= []
13
+ instance_exec(&block)
9
14
  end
10
15
 
16
+ # Executes the code inside a block after the record is *created*
11
17
  def on_create(&block)
12
18
  store(:create, Rules::CreateRule.new(block))
13
19
  end
14
20
 
21
+ # Executes the code inside a block after the record is *destroyed*
15
22
  def on_destroy(&block)
16
23
  store(:destroy, Rules::DestroyRule.new(block))
17
24
  end
18
25
 
26
+ # @param attributes [Symbols] - list of attribute names (e.g. :user_id, :user_group_id ...)
27
+ # @param values [Array(2)] - array of two values (before and after)
28
+ # Launches code inside a block if an object changes any of attributes according
29
+ # to the pattern which is described in the brackets (value before -> value after)
30
+ #
31
+ # This uses `===` method to compare values.
32
+ # @example Add hook on update any of attributes according to the pattern
33
+ # on_update :address, :phone_number, [CustomActiveRecordObserver::NotNill,
34
+ # CustomActiveRecordObserver::NotNill] do |user|
35
+ # # notify system that user contact info was changed
36
+ # end
37
+ #
38
+ # Will be launched only if user_group_id was changed from `nil` to anything but nil.
39
+ # in the array [] there are two values that are compared with the real changes
40
+ # to detect if they maches.
41
+ # @example Track nil -> not nill changes of user_group_id attribute
42
+ # on_update :user_group_id, [nil, CustomActiveRecordObserver::NotNill] do |user|
43
+ # # here we know that some user_group was assigned to user
44
+ # end
45
+ # @example Other examples of usage:
46
+ # on_update :user_group_id, [1, 2] { |user| ... }
47
+ # on_update :user_group_id, [1, Integer] { |user| ... }
19
48
  def on_update(*attributes, &block)
20
49
  pattern = attributes.pop if attributes.last.is_a?(Array)
21
50
 
@@ -24,14 +53,32 @@ module CustomActiveRecordObserver
24
53
  end
25
54
  end
26
55
 
56
+ # Executes the code inside a block when the specific attributes are *updated*
57
+ # from <tt>nil</tt> to some <tt>not nil</tt> value
58
+ # @example
59
+ # on_add :user_group_id do |user|
60
+ # # here we know that user_group_id was nil, but now it is not
61
+ # end
27
62
  def on_add(*attributes, &block)
28
63
  on_update(*attributes.push([nil, NotNil]), &block)
29
64
  end
30
65
 
66
+ # Executes the code inside a block when the specific attributes are *updated*
67
+ # from some <tt>not nil</tt> value to <tt>nil</tt>
68
+ # @example
69
+ # on_remove :user_group_id do |user|
70
+ # # here we know that user_group_id is equal to nil now
71
+ # end
31
72
  def on_remove(*attributes, &block)
32
73
  on_update(*attributes.push([NotNil, nil]), &block)
33
74
  end
34
75
 
76
+ # Executes the code inside a block when the specific attributes are *updated*
77
+ # from some <tt>not nil</tt> to another <tt>not nil</tt>
78
+ # @example
79
+ # on_change :user_group_id do |user|
80
+ # # user was assigned to another user group
81
+ # end
35
82
  def on_change(*attributes, &block)
36
83
  on_update(*attributes.push([NotNil, NotNil]), &block)
37
84
  end
@@ -1,5 +1,9 @@
1
+ # @title CustomActiveRecordObserver::NotNil
1
2
  module CustomActiveRecordObserver
3
+ # A module to represent any of values that are not equal to +nil+
2
4
  module NotNil
5
+ # @param other [Any]
6
+ # @return [Boolean]
3
7
  def self.===(other)
4
8
  super || !other.nil?
5
9
  end
@@ -1,5 +1,8 @@
1
1
  module CustomActiveRecordObserver
2
2
  module Observable
3
+ # The base module which is included to all classes that are observed.
4
+ # It adds AR after commit callbacks to class instances and handles
5
+ # the results with <tt>CustomActiveRecordObserver::Handler</tt>
3
6
  module Base
4
7
  extend ActiveSupport::Concern
5
8
 
@@ -20,6 +20,10 @@ module CustomActiveRecordObserver
20
20
  end
21
21
 
22
22
  def add_rule(class_name, action, rules, handler)
23
+ unless class_name.is_a?(Symbol)
24
+ class_name = class_name.to_s.to_sym
25
+ end
26
+
23
27
  schema[class_name] ||= {}
24
28
  schema[class_name][action] ||= []
25
29
 
@@ -1,3 +1,3 @@
1
1
  module CustomActiveRecordObserver
2
- VERSION = '0.3.0'.freeze
2
+ VERSION = '0.3.1'.freeze
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: custom_active_record_observer
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.3.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - OnApp Ltd.
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-02-15 00:00:00.000000000 Z
11
+ date: 2018-03-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord