custom_active_record_observer 0.1.0
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 +7 -0
- data/README.md +25 -0
- data/Rakefile +20 -0
- data/lib/custom_active_record_observer.rb +16 -0
- data/lib/custom_active_record_observer/all.rb +9 -0
- data/lib/custom_active_record_observer/changes_tracker.rb +16 -0
- data/lib/custom_active_record_observer/dsl.rb +45 -0
- data/lib/custom_active_record_observer/engine.rb +11 -0
- data/lib/custom_active_record_observer/handler.rb +17 -0
- data/lib/custom_active_record_observer/models_extender.rb +9 -0
- data/lib/custom_active_record_observer/not_nil.rb +7 -0
- data/lib/custom_active_record_observer/observable.rb +11 -0
- data/lib/custom_active_record_observer/rules/all.rb +3 -0
- data/lib/custom_active_record_observer/rules/base_rule.rb +19 -0
- data/lib/custom_active_record_observer/rules/create_rule.rb +10 -0
- data/lib/custom_active_record_observer/rules/destroy_rule.rb +10 -0
- data/lib/custom_active_record_observer/rules/update_rule.rb +32 -0
- data/lib/custom_active_record_observer/schema.rb +44 -0
- data/lib/custom_active_record_observer/version.rb +3 -0
- metadata +158 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: de2f53459d0571d2542de638f2f10e80637d20ff
|
4
|
+
data.tar.gz: f1a58b6f3014fbbbfb96eee376fa1a8de82c7110
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 91aaa0ca81ac659ef16b3f58169e3069ecae1d549ffe5b959391b87c34fc5b17234aeaf6b29c308cf0d60e23a6ce3ad52acdefebd56b5643210d5313426d161c
|
7
|
+
data.tar.gz: 7deb22d765fffe58c49902a163604823e88e22172fd5b8e0bdd63dcce2fe7b53960dd7e3a622a24c0cca60449c03154b089938812562e7885788d57dd5866f96
|
data/README.md
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# CustomActiveRecordObserver
|
2
|
+
|
3
|
+
`custom_active_record_observer` is a DSL to track create/update/destroy operations based on AR callbacks.
|
4
|
+
|
5
|
+
Its a simple DSL similar to the code below:
|
6
|
+
|
7
|
+
```
|
8
|
+
CustomActiveRecordObserver.observe :EngagedUser, :CompanyOwner, handler: CustomerEventPublisher.new do
|
9
|
+
on_create do |customer|
|
10
|
+
Events::CustomerSignedUp.new(data: {customer_id: customer.id}
|
11
|
+
end
|
12
|
+
|
13
|
+
on_destroy do |customer|
|
14
|
+
Events::CustomerSignedOut.new(data: {…})
|
15
|
+
end
|
16
|
+
|
17
|
+
on_change :group_id do |customer|
|
18
|
+
Events::CustomerGroupChanged.new(data: {…})
|
19
|
+
end
|
20
|
+
…
|
21
|
+
end
|
22
|
+
```
|
23
|
+
|
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.
|
data/Rakefile
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
#!/usr/bin/env rake
|
2
|
+
begin
|
3
|
+
require 'bundler/setup'
|
4
|
+
rescue LoadError
|
5
|
+
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
6
|
+
end
|
7
|
+
|
8
|
+
begin
|
9
|
+
require 'rdoc/task'
|
10
|
+
rescue LoadError
|
11
|
+
require 'rdoc/rdoc'
|
12
|
+
require 'rake/rdoctask'
|
13
|
+
RDoc::Task = Rake::RDocTask
|
14
|
+
end
|
15
|
+
|
16
|
+
APP_RAKEFILE = File.expand_path('spec/dummy/Rakefile', __dir__)
|
17
|
+
load 'rails/tasks/engine.rake'
|
18
|
+
|
19
|
+
Bundler::GemHelper.install_tasks
|
20
|
+
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'active_support/dependencies'
|
2
|
+
|
3
|
+
module CustomActiveRecordObserver
|
4
|
+
require_relative 'custom_active_record_observer/all'
|
5
|
+
|
6
|
+
mattr_accessor :schema
|
7
|
+
@@schema = Schema.new
|
8
|
+
|
9
|
+
def self.observe(*class_names, handler:, &block)
|
10
|
+
class_names.each do |class_name|
|
11
|
+
DSL.new(block).actions_and_rules.each do |(action, rule)|
|
12
|
+
schema.add_rule(class_name, action, rule, handler)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,9 @@
|
|
1
|
+
require_relative 'models_extender'
|
2
|
+
require_relative 'observable'
|
3
|
+
require_relative 'engine'
|
4
|
+
require_relative 'rules/all'
|
5
|
+
require_relative 'not_nil'
|
6
|
+
require_relative 'handler'
|
7
|
+
require_relative 'dsl'
|
8
|
+
require_relative 'schema'
|
9
|
+
require_relative 'changes_tracker'
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module CustomActiveRecordObserver
|
2
|
+
module ChangesTracker
|
3
|
+
def self.[](*method_names)
|
4
|
+
Module.new do
|
5
|
+
method_names.each do |name|
|
6
|
+
define_method name do |*args|
|
7
|
+
@_active_record_observer_changes ||= {}
|
8
|
+
@_active_record_observer_changes.merge!(changes)
|
9
|
+
|
10
|
+
super(*args)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module CustomActiveRecordObserver
|
2
|
+
class DSL
|
3
|
+
def initialize(block)
|
4
|
+
instance_exec(&block)
|
5
|
+
end
|
6
|
+
|
7
|
+
def actions_and_rules
|
8
|
+
@actions_and_rules ||= []
|
9
|
+
end
|
10
|
+
|
11
|
+
def on_create(&block)
|
12
|
+
store(:create, Rules::CreateRule.new(block))
|
13
|
+
end
|
14
|
+
|
15
|
+
def on_destroy(&block)
|
16
|
+
store(:destroy, Rules::DestroyRule.new(block))
|
17
|
+
end
|
18
|
+
|
19
|
+
def on_update(*attributes, &block)
|
20
|
+
pattern = attributes.pop if attributes.last.is_a?(Array)
|
21
|
+
|
22
|
+
attributes.each do |attribute|
|
23
|
+
store(:update, Rules::UpdateRule.new(block, attribute: attribute, pattern: pattern))
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def on_add(*attributes, &block)
|
28
|
+
on_update(*attributes.push([nil, NotNil]), &block)
|
29
|
+
end
|
30
|
+
|
31
|
+
def on_remove(*attributes, &block)
|
32
|
+
on_update(*attributes.push([NotNil, nil]), &block)
|
33
|
+
end
|
34
|
+
|
35
|
+
def on_change(*attributes, &block)
|
36
|
+
on_update(*attributes.push([NotNil, NotNil]), &block)
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def store(action, rule)
|
42
|
+
actions_and_rules << [action, rule]
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
module CustomActiveRecordObserver
|
2
|
+
class Engine < ::Rails::Engine
|
3
|
+
isolate_namespace CustomActiveRecordObserver
|
4
|
+
|
5
|
+
config.to_prepare do
|
6
|
+
CustomActiveRecordObserver.schema.freeze
|
7
|
+
|
8
|
+
CustomActiveRecordObserver::ModelsExtender.(CustomActiveRecordObserver.schema)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module CustomActiveRecordObserver
|
2
|
+
module Handler
|
3
|
+
def self.call(target, action, _changes, schema: CustomActiveRecordObserver.schema)
|
4
|
+
# Some AR libraries reload AR object in the callbacks.
|
5
|
+
# Hence, ActiveModel::Dirty model changes hash becomes empty.
|
6
|
+
# @_active_record_observer_changes is needed to that changes not be missed
|
7
|
+
changes =
|
8
|
+
(target.instance_variable_get(:@_active_record_observer_changes) || {}).merge(_changes)
|
9
|
+
|
10
|
+
ActiveRecord::Base.transaction do
|
11
|
+
schema.
|
12
|
+
get_rules(target.class.name, action, changes).
|
13
|
+
each { |rule, handler| handler.(rule.(target)) }
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
module CustomActiveRecordObserver
|
2
|
+
module Observable
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
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
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module CustomActiveRecordObserver
|
2
|
+
module Rules
|
3
|
+
class BaseRule
|
4
|
+
attr_reader :block
|
5
|
+
|
6
|
+
def initialize(block)
|
7
|
+
@block = block
|
8
|
+
end
|
9
|
+
|
10
|
+
def call(target)
|
11
|
+
block.call(target)
|
12
|
+
end
|
13
|
+
|
14
|
+
def allowed?(changes)
|
15
|
+
raise NotImplementedError
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module CustomActiveRecordObserver
|
2
|
+
module Rules
|
3
|
+
class UpdateRule < BaseRule
|
4
|
+
attr_reader :attribute, :pattern
|
5
|
+
|
6
|
+
def initialize(block, attribute: nil, pattern: nil)
|
7
|
+
super(block)
|
8
|
+
|
9
|
+
if pattern.is_a?(Array) && pattern.size != 2
|
10
|
+
raise ArgumentError, "#{ pattern } is expected to be an array of two elements"
|
11
|
+
end
|
12
|
+
|
13
|
+
@attribute = attribute
|
14
|
+
@pattern = pattern
|
15
|
+
end
|
16
|
+
|
17
|
+
def allowed?(changes)
|
18
|
+
attribute &&
|
19
|
+
changes.has_key?(attribute) &&
|
20
|
+
matches_pattern?(*changes.fetch(attribute))
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def matches_pattern?(value_was, value)
|
26
|
+
return true unless pattern # no pattern => allowed for all
|
27
|
+
|
28
|
+
pattern.first === value_was && pattern.last === value
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module CustomActiveRecordObserver
|
2
|
+
class Schema
|
3
|
+
attr_reader :schema
|
4
|
+
|
5
|
+
delegate :fetch, to: :schema
|
6
|
+
|
7
|
+
# {
|
8
|
+
# 'User' =>
|
9
|
+
# {
|
10
|
+
# update: [[rule_1, handler], [rule_2, handler]],
|
11
|
+
# create: [[rule_3, handler]]
|
12
|
+
# }
|
13
|
+
# 'Article' =>
|
14
|
+
# {
|
15
|
+
# destroy: [[rule5, handler]],
|
16
|
+
# }
|
17
|
+
# }
|
18
|
+
def initialize(schema = {})
|
19
|
+
@schema = schema
|
20
|
+
end
|
21
|
+
|
22
|
+
def add_rule(class_name, action, rules, handler)
|
23
|
+
schema[class_name] ||= {}
|
24
|
+
schema[class_name][action] ||= []
|
25
|
+
|
26
|
+
Array(rules).each do |rule|
|
27
|
+
schema[class_name][action] << [rule, handler]
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def get_rules(class_name, action, raw_changes = {})
|
32
|
+
changes = raw_changes.symbolize_keys
|
33
|
+
|
34
|
+
schema.fetch(class_name.to_sym, {}).
|
35
|
+
fetch(action, []).select do |(rule, handler)|
|
36
|
+
rule.allowed?(changes)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def classes
|
41
|
+
schema.keys.map { |name| name.to_s.constantize }
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
metadata
ADDED
@@ -0,0 +1,158 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: custom_active_record_observer
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- OnApp Ltd.
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2017-11-21 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: activerecord
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '3.2'
|
20
|
+
- - "<"
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: '6'
|
23
|
+
type: :runtime
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '3.2'
|
30
|
+
- - "<"
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '6'
|
33
|
+
- !ruby/object:Gem::Dependency
|
34
|
+
name: railties
|
35
|
+
requirement: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - ">="
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: '3.2'
|
40
|
+
- - "<"
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
version: '6'
|
43
|
+
type: :runtime
|
44
|
+
prerelease: false
|
45
|
+
version_requirements: !ruby/object:Gem::Requirement
|
46
|
+
requirements:
|
47
|
+
- - ">="
|
48
|
+
- !ruby/object:Gem::Version
|
49
|
+
version: '3.2'
|
50
|
+
- - "<"
|
51
|
+
- !ruby/object:Gem::Version
|
52
|
+
version: '6'
|
53
|
+
- !ruby/object:Gem::Dependency
|
54
|
+
name: rspec
|
55
|
+
requirement: !ruby/object:Gem::Requirement
|
56
|
+
requirements:
|
57
|
+
- - ">="
|
58
|
+
- !ruby/object:Gem::Version
|
59
|
+
version: '0'
|
60
|
+
type: :development
|
61
|
+
prerelease: false
|
62
|
+
version_requirements: !ruby/object:Gem::Requirement
|
63
|
+
requirements:
|
64
|
+
- - ">="
|
65
|
+
- !ruby/object:Gem::Version
|
66
|
+
version: '0'
|
67
|
+
- !ruby/object:Gem::Dependency
|
68
|
+
name: sqlite3
|
69
|
+
requirement: !ruby/object:Gem::Requirement
|
70
|
+
requirements:
|
71
|
+
- - ">="
|
72
|
+
- !ruby/object:Gem::Version
|
73
|
+
version: '0'
|
74
|
+
type: :development
|
75
|
+
prerelease: false
|
76
|
+
version_requirements: !ruby/object:Gem::Requirement
|
77
|
+
requirements:
|
78
|
+
- - ">="
|
79
|
+
- !ruby/object:Gem::Version
|
80
|
+
version: '0'
|
81
|
+
- !ruby/object:Gem::Dependency
|
82
|
+
name: fuubar
|
83
|
+
requirement: !ruby/object:Gem::Requirement
|
84
|
+
requirements:
|
85
|
+
- - ">="
|
86
|
+
- !ruby/object:Gem::Version
|
87
|
+
version: '0'
|
88
|
+
type: :development
|
89
|
+
prerelease: false
|
90
|
+
version_requirements: !ruby/object:Gem::Requirement
|
91
|
+
requirements:
|
92
|
+
- - ">="
|
93
|
+
- !ruby/object:Gem::Version
|
94
|
+
version: '0'
|
95
|
+
- !ruby/object:Gem::Dependency
|
96
|
+
name: pry
|
97
|
+
requirement: !ruby/object:Gem::Requirement
|
98
|
+
requirements:
|
99
|
+
- - ">="
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: '0'
|
102
|
+
type: :development
|
103
|
+
prerelease: false
|
104
|
+
version_requirements: !ruby/object:Gem::Requirement
|
105
|
+
requirements:
|
106
|
+
- - ">="
|
107
|
+
- !ruby/object:Gem::Version
|
108
|
+
version: '0'
|
109
|
+
description:
|
110
|
+
email:
|
111
|
+
- support@onapp.com
|
112
|
+
executables: []
|
113
|
+
extensions: []
|
114
|
+
extra_rdoc_files: []
|
115
|
+
files:
|
116
|
+
- README.md
|
117
|
+
- Rakefile
|
118
|
+
- lib/custom_active_record_observer.rb
|
119
|
+
- lib/custom_active_record_observer/all.rb
|
120
|
+
- lib/custom_active_record_observer/changes_tracker.rb
|
121
|
+
- lib/custom_active_record_observer/dsl.rb
|
122
|
+
- lib/custom_active_record_observer/engine.rb
|
123
|
+
- lib/custom_active_record_observer/handler.rb
|
124
|
+
- lib/custom_active_record_observer/models_extender.rb
|
125
|
+
- lib/custom_active_record_observer/not_nil.rb
|
126
|
+
- lib/custom_active_record_observer/observable.rb
|
127
|
+
- lib/custom_active_record_observer/rules/all.rb
|
128
|
+
- lib/custom_active_record_observer/rules/base_rule.rb
|
129
|
+
- lib/custom_active_record_observer/rules/create_rule.rb
|
130
|
+
- lib/custom_active_record_observer/rules/destroy_rule.rb
|
131
|
+
- lib/custom_active_record_observer/rules/update_rule.rb
|
132
|
+
- lib/custom_active_record_observer/schema.rb
|
133
|
+
- lib/custom_active_record_observer/version.rb
|
134
|
+
homepage:
|
135
|
+
licenses:
|
136
|
+
- Apache 2.0
|
137
|
+
metadata: {}
|
138
|
+
post_install_message:
|
139
|
+
rdoc_options: []
|
140
|
+
require_paths:
|
141
|
+
- lib
|
142
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
143
|
+
requirements:
|
144
|
+
- - ">="
|
145
|
+
- !ruby/object:Gem::Version
|
146
|
+
version: '0'
|
147
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
148
|
+
requirements:
|
149
|
+
- - ">="
|
150
|
+
- !ruby/object:Gem::Version
|
151
|
+
version: '0'
|
152
|
+
requirements: []
|
153
|
+
rubyforge_project:
|
154
|
+
rubygems_version: 2.5.2
|
155
|
+
signing_key:
|
156
|
+
specification_version: 4
|
157
|
+
summary: CustomActiveRecordObserver
|
158
|
+
test_files: []
|