custom_active_record_observer 0.2.0 → 0.5.0

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
- SHA1:
3
- metadata.gz: b13cc7f8b0aceb704bd1d8e1e1fad0835574c65d
4
- data.tar.gz: 219db492b79a927aaecc9afe16d5d4d8fc6270e2
2
+ SHA256:
3
+ metadata.gz: 6644843d956acabec36f996af2a3add3756ee78aaeb5cf1b9286bbf1fb3edfc3
4
+ data.tar.gz: 6f4f323b250aac555eb294762811fe0c1d52c0f0288d03a41657489c2b804022
5
5
  SHA512:
6
- metadata.gz: 3d0ad899787e4a3e447cc3255b373875f541d731698bd200f48688d7645132e6bb83593657daa2b1a0929c563a5eb0ec69470ef9c8bc9655864fced1f03d26d4
7
- data.tar.gz: ef7562bcbce78b0b2a0cc29ceb64ba14d8222c31621d761bb63e26c0152a27862bdb415e6fd019f429e82fa0a1217c6a22bfbabbb218de13a1ecd1610865082a
6
+ metadata.gz: 6cc3163d3cf8ba51021ce31681945d301413ee30aff9387014c86c8f522d1cca62331f9ae7d25f910b6020aa06a72ab29956e6302133a48ec1be064df5d48efe
7
+ data.tar.gz: 30bbcc652985d669ef52e620a9b0e55d136d7feabdffd214b83db4ce1678b33589fe93830dee145d082f8944ec434dc428e7296e597e2fd885511d4927871a34
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,4 +1,6 @@
1
1
  require_relative 'models_extender'
2
+ require_relative 'observable/base'
3
+ require_relative 'observable/paranoid'
2
4
  require_relative 'observable'
3
5
  require_relative 'engine'
4
6
  require_relative 'rules/all'
@@ -5,7 +5,7 @@ module CustomActiveRecordObserver
5
5
  method_names.each do |name|
6
6
  define_method name do |*args|
7
7
  @_active_record_observer_changes ||= {}
8
- @_active_record_observer_changes.merge!(changes)
8
+ @_active_record_observer_changes.merge!(previous_changes)
9
9
 
10
10
  super(*args)
11
11
  end
@@ -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
@@ -3,9 +3,15 @@ module CustomActiveRecordObserver
3
3
  extend ActiveSupport::Concern
4
4
 
5
5
  included do
6
- after_commit -> { CustomActiveRecordObserver::Handler.(self, :create, previous_changes) }, on: :create
7
- after_commit -> { CustomActiveRecordObserver::Handler.(self, :update, previous_changes) }, on: :update
8
- after_commit -> { CustomActiveRecordObserver::Handler.(self, :destroy, previous_changes) }, on: :destroy
6
+ if defined?(CollectiveIdea::Acts::NestedSet) && self < CollectiveIdea::Acts::NestedSet::Model
7
+ prepend CustomActiveRecordObserver::ChangesTracker[:set_depth!]
8
+ end
9
+
10
+ if try(:paranoid?)
11
+ include Paranoid
12
+ else
13
+ include Base
14
+ end
9
15
  end
10
16
  end
11
17
  end
@@ -0,0 +1,16 @@
1
+ module CustomActiveRecordObserver
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>
6
+ module Base
7
+ extend ActiveSupport::Concern
8
+
9
+ included do
10
+ after_commit -> { CustomActiveRecordObserver::Handler.(self, :create, previous_changes) }, on: :create
11
+ after_commit -> { CustomActiveRecordObserver::Handler.(self, :update, previous_changes) }, on: :update
12
+ after_commit -> { CustomActiveRecordObserver::Handler.(self, :destroy, previous_changes) }, on: :destroy
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,16 @@
1
+ module CustomActiveRecordObserver
2
+ module Observable
3
+ module Paranoid
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+ after_commit -> { CustomActiveRecordObserver::Handler.(self, :create, previous_changes) },
8
+ on: :create
9
+ after_commit -> { CustomActiveRecordObserver::Handler.(self, :update, previous_changes) },
10
+ on: :update
11
+ after_destroy -> { CustomActiveRecordObserver::Handler.(self, :destroy, previous_changes) },
12
+ if: -> { deleted_at }
13
+ end
14
+ end
15
+ end
16
+ end
@@ -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.2.0'.freeze
2
+ VERSION = '0.5.0'.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.2.0
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - OnApp Ltd.
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-11-22 00:00:00.000000000 Z
11
+ date: 2020-08-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -16,40 +16,28 @@ dependencies:
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: '3.2'
20
- - - "<"
21
- - !ruby/object:Gem::Version
22
- version: '6'
19
+ version: '5.2'
23
20
  type: :runtime
24
21
  prerelease: false
25
22
  version_requirements: !ruby/object:Gem::Requirement
26
23
  requirements:
27
24
  - - ">="
28
25
  - !ruby/object:Gem::Version
29
- version: '3.2'
30
- - - "<"
31
- - !ruby/object:Gem::Version
32
- version: '6'
26
+ version: '5.2'
33
27
  - !ruby/object:Gem::Dependency
34
28
  name: railties
35
29
  requirement: !ruby/object:Gem::Requirement
36
30
  requirements:
37
31
  - - ">="
38
32
  - !ruby/object:Gem::Version
39
- version: '3.2'
40
- - - "<"
41
- - !ruby/object:Gem::Version
42
- version: '6'
33
+ version: '5.2'
43
34
  type: :runtime
44
35
  prerelease: false
45
36
  version_requirements: !ruby/object:Gem::Requirement
46
37
  requirements:
47
38
  - - ">="
48
39
  - !ruby/object:Gem::Version
49
- version: '3.2'
50
- - - "<"
51
- - !ruby/object:Gem::Version
52
- version: '6'
40
+ version: '5.2'
53
41
  - !ruby/object:Gem::Dependency
54
42
  name: rspec
55
43
  requirement: !ruby/object:Gem::Requirement
@@ -78,6 +66,34 @@ dependencies:
78
66
  - - ">="
79
67
  - !ruby/object:Gem::Version
80
68
  version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: appraisal
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: pry
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
81
97
  description: |2
82
98
  This gem delivers a simple DSL based on the after_commit callback
83
99
  which can be used as an alternative to hooks from inside the model
@@ -98,6 +114,8 @@ files:
98
114
  - lib/custom_active_record_observer/models_extender.rb
99
115
  - lib/custom_active_record_observer/not_nil.rb
100
116
  - lib/custom_active_record_observer/observable.rb
117
+ - lib/custom_active_record_observer/observable/base.rb
118
+ - lib/custom_active_record_observer/observable/paranoid.rb
101
119
  - lib/custom_active_record_observer/rules/all.rb
102
120
  - lib/custom_active_record_observer/rules/base_rule.rb
103
121
  - lib/custom_active_record_observer/rules/create_rule.rb
@@ -117,7 +135,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
117
135
  requirements:
118
136
  - - ">="
119
137
  - !ruby/object:Gem::Version
120
- version: 2.2.0
138
+ version: 2.5.6
121
139
  required_rubygems_version: !ruby/object:Gem::Requirement
122
140
  requirements:
123
141
  - - ">="
@@ -125,7 +143,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
125
143
  version: '0'
126
144
  requirements: []
127
145
  rubyforge_project:
128
- rubygems_version: 2.5.2
146
+ rubygems_version: 2.7.6.2
129
147
  signing_key:
130
148
  specification_version: 4
131
149
  summary: A small handy library to track create/update/destroy actions on active_record