activerecord-reactor 1.0.0 → 1.0.2
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.
- data/CHANGELOG +4 -0
- data/README.md +78 -3
- data/Rakefile +2 -2
- data/lib/active_record/reactor/callbacks.rb +22 -2
- data/lib/active_record/reactor/version.rb +1 -1
- data/lib/active_record/reactor.rb +43 -4
- data/lib/activerecord-reactor.rb +3 -3
- data/test/cases/reactor_test.rb +9 -1
- data/test/models/cherry.rb +1 -1
- metadata +25 -49
- data/lib/active_record/active_record.rb +0 -12
data/CHANGELOG
ADDED
data/README.md
CHANGED
@@ -1,4 +1,79 @@
|
|
1
|
-
activerecord-reactor
|
2
|
-
====================
|
1
|
+
[](http://badge.fury.io/rb/activerecord-reactor) [](https://codeclimate.com/github/mtgrosser/activerecord-reactor)
|
3
2
|
|
4
|
-
|
3
|
+
Reactor - Controlled reactions on ActiveRecord callbacks
|
4
|
+
========================================================
|
5
|
+
|
6
|
+
ActiveRecord Reactors provide a defined way to react on default or custom Active Record callbacks. You may think of them as Observers without the magic, and without the hassle.
|
7
|
+
|
8
|
+
## Install
|
9
|
+
```
|
10
|
+
# In your Gemfile
|
11
|
+
gem 'activerecord-reactor'
|
12
|
+
```
|
13
|
+
|
14
|
+
## Usage
|
15
|
+
|
16
|
+
Define a custom reactor class, where you would use an Observer:
|
17
|
+
|
18
|
+
```ruby
|
19
|
+
class YummyReactor < ActiveRecord::Reactor
|
20
|
+
after_create(record)
|
21
|
+
puts "Yummy, #{record.color} #{record.class.name} created!" if record.is_a?(Fruit)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
```
|
25
|
+
|
26
|
+
Connect your models to the reactor:
|
27
|
+
|
28
|
+
```ruby
|
29
|
+
class Fruit < ActiveRecord::Base
|
30
|
+
attr_accessor :peel
|
31
|
+
|
32
|
+
reactor :yummy
|
33
|
+
|
34
|
+
def color; end
|
35
|
+
end
|
36
|
+
```
|
37
|
+
|
38
|
+
You can also use custom model callbacks:
|
39
|
+
|
40
|
+
```ruby
|
41
|
+
class Tidy < ActiveRecord::Reactor
|
42
|
+
def before_peel(record)
|
43
|
+
record.wash!
|
44
|
+
end
|
45
|
+
|
46
|
+
def after_peel(record)
|
47
|
+
GarbageCollector.dispose(record.peel)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
class Banana < Fruit
|
52
|
+
# Use instead of define_model_callbacks if you want to register
|
53
|
+
# the callback with reactors
|
54
|
+
define_reactor_callbacks :peel
|
55
|
+
|
56
|
+
# You can use the actual class if you do not want the reactor name end with 'Reactor'
|
57
|
+
reactor Tidy
|
58
|
+
|
59
|
+
def color
|
60
|
+
'yellow'
|
61
|
+
end
|
62
|
+
|
63
|
+
def peel!
|
64
|
+
run_callbacks :peel do
|
65
|
+
self.peel = Object.new
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
```
|
70
|
+
|
71
|
+
Finally, you can temporarily shutdown any reactions on a reactor:
|
72
|
+
|
73
|
+
```ruby
|
74
|
+
class Apple < Fruit; end
|
75
|
+
|
76
|
+
YummyReactor.scram do
|
77
|
+
Apple.create! # hide this one from YummyReactor
|
78
|
+
end
|
79
|
+
```
|
data/Rakefile
CHANGED
@@ -15,8 +15,8 @@ end
|
|
15
15
|
desc 'Generate documentation'
|
16
16
|
Rake::RDocTask.new(:rdoc) do |rdoc|
|
17
17
|
rdoc.rdoc_dir = 'rdoc'
|
18
|
-
rdoc.title = '
|
18
|
+
rdoc.title = 'ActiveRecord::Reactor'
|
19
19
|
rdoc.options << '--line-numbers' << '--inline-source'
|
20
|
-
rdoc.rdoc_files.include('README')
|
20
|
+
rdoc.rdoc_files.include('README.md')
|
21
21
|
rdoc.rdoc_files.include('lib/**/*.rb')
|
22
22
|
end
|
@@ -4,7 +4,7 @@ module ActiveRecord
|
|
4
4
|
|
5
5
|
module Callbacks
|
6
6
|
|
7
|
-
def self.included(base)
|
7
|
+
def self.included(base) # :nodoc:
|
8
8
|
base.extend ClassMethods
|
9
9
|
base.class_attribute :reactor_callbacks
|
10
10
|
base.reactor_callbacks = ActiveRecord::Base::CALLBACKS.dup
|
@@ -12,11 +12,18 @@ module ActiveRecord
|
|
12
12
|
|
13
13
|
module ClassMethods
|
14
14
|
|
15
|
+
# Define a custom model callback, which is available to reactors registered with
|
16
|
+
# that model. Works like <tt>ActiveModel::define_model_callbacks</tt>, but does not
|
17
|
+
# define <tt>around</tt> callbacks by default.
|
18
|
+
#
|
19
|
+
# Make sure to define any custom callbacks before registering any reactors
|
20
|
+
# with your model that should react on that callback.
|
21
|
+
#
|
15
22
|
def define_reactor_callbacks(*args)
|
16
23
|
options = args.extract_options!
|
17
24
|
options[:only] ||= [:before, :after] # TODO: implement around callbacks
|
18
25
|
types = Array.wrap(options[:only])
|
19
|
-
define_model_callbacks(*(args << options))
|
26
|
+
define_model_callbacks(*(args.dup << options))
|
20
27
|
def args.combine(other, &block)
|
21
28
|
return product(other) unless block_given?
|
22
29
|
product(other).inject([]) { |result, ab| result << yield(ab.first, ab.last) }
|
@@ -24,6 +31,19 @@ module ActiveRecord
|
|
24
31
|
self.reactor_callbacks = (reactor_callbacks + args.combine(types) { |arg, type| :"#{type}_#{arg}" }).uniq
|
25
32
|
end
|
26
33
|
|
34
|
+
# Register a reactor with the model. Note that only callbacks defined on the reactor at
|
35
|
+
# the time of registration will be called.
|
36
|
+
#
|
37
|
+
# <tt>klass_or_name<tt> can be an actual reactor class, or a string or symbol.
|
38
|
+
#
|
39
|
+
# class Banana < ActiveRecord::Base
|
40
|
+
# # register YummyReactor
|
41
|
+
# reactor :yummy
|
42
|
+
#
|
43
|
+
# # register reactor CustomName
|
44
|
+
# reactor CustomName
|
45
|
+
# end
|
46
|
+
#
|
27
47
|
def reactor(klass_or_name)
|
28
48
|
reactor = klass_or_name.is_a?(Class) ? klass_or_name : "#{klass_or_name.to_s.camelize}Reactor".constantize
|
29
49
|
(reactor.callbacks & self.reactor_callbacks).each do |callback|
|
@@ -1,20 +1,59 @@
|
|
1
1
|
require 'singleton'
|
2
2
|
|
3
3
|
module ActiveRecord
|
4
|
-
|
4
|
+
# = Active Record Reactors
|
5
|
+
#
|
6
|
+
# ActiveRecord Reactors provide a defined way to react on default or custom <tt>ActiveRecord::Callbacks</tt>.
|
7
|
+
# You may think of a Reactor as an <tt>Observer</tt> which is explicitly registered with one or more models,
|
8
|
+
# and does not define any magic methods.
|
9
|
+
#
|
10
|
+
# When a Reactor is registered with a model, it checks which of the model's callbacks are also defined as
|
11
|
+
# reactor instance methods, and adds these to the model's callback chain.
|
12
|
+
#
|
13
|
+
# Reactor callbacks will always return <tt>true</tt>, therefore it is not possible to halt the callback
|
14
|
+
# chain from a reactor. If you want to do that, you should consider moving the code inside the model.
|
15
|
+
#
|
16
|
+
# Examples:
|
17
|
+
#
|
18
|
+
# class YummyReactor < ActiveRecord::Reactor
|
19
|
+
# after_create(record)
|
20
|
+
# puts "Yummy, #{record.color} #{record.class.name} created!" if record.is_a?(Fruit)
|
21
|
+
# end
|
22
|
+
# end
|
23
|
+
#
|
24
|
+
# class Fruit < ActiveRecord::Base
|
25
|
+
# attr_accessor :peel
|
26
|
+
#
|
27
|
+
# # Connect your model to the reactor
|
28
|
+
# reactor :yummy
|
29
|
+
#
|
30
|
+
# def color; end
|
31
|
+
# end
|
32
|
+
#
|
33
|
+
# == Reactor scramming
|
34
|
+
#
|
35
|
+
# Reactors can be halted temporarily using the <tt>scram</tt> class method.
|
36
|
+
#
|
37
|
+
# class Apple < Fruit; end
|
38
|
+
#
|
39
|
+
# YummyReactor.scram do
|
40
|
+
# Apple.create! # do not trigger reaction on YummyReactor
|
41
|
+
# end
|
42
|
+
#
|
5
43
|
class Reactor
|
6
44
|
|
7
45
|
include Singleton
|
8
46
|
|
9
47
|
class << self
|
10
|
-
def callbacks
|
48
|
+
def callbacks # :nodoc:
|
11
49
|
(self.public_instance_methods - ActiveRecord::Reactor.public_instance_methods).grep(/\A(before|around|after)_.+/)
|
12
50
|
end
|
13
|
-
|
14
|
-
def scrammed?
|
51
|
+
|
52
|
+
def scrammed? # :nodoc:
|
15
53
|
!!@scrammed
|
16
54
|
end
|
17
55
|
|
56
|
+
# Stops all reactions while inside the (required) block.
|
18
57
|
def scram(&block)
|
19
58
|
previously_scrammed = @scrammed
|
20
59
|
@scrammed = true
|
data/lib/activerecord-reactor.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
require 'active_record'
|
2
|
-
|
3
|
-
|
4
|
-
|
2
|
+
require_relative 'active_record/reactor'
|
3
|
+
require_relative 'active_record/reactor/callbacks'
|
4
|
+
require_relative 'active_record/reactor/version'
|
5
5
|
|
6
6
|
ActiveRecord::Base.class_eval do
|
7
7
|
include ActiveRecord::Reactor::Callbacks
|
data/test/cases/reactor_test.rb
CHANGED
@@ -9,6 +9,14 @@ class ReactorTest < ActiveSupport::TestCase
|
|
9
9
|
PeelReactor.calls.clear
|
10
10
|
end
|
11
11
|
|
12
|
+
test 'Callbacks are defined' do
|
13
|
+
klass = Class.new(ActiveRecord::Base)
|
14
|
+
assert_equal ActiveRecord::Base::CALLBACKS, klass.reactor_callbacks
|
15
|
+
assert_equal ActiveRecord::Base::CALLBACKS + [:before_peel, :after_peel], Fruit.reactor_callbacks
|
16
|
+
assert_equal ActiveRecord::Base::CALLBACKS + [:before_peel, :after_peel, :before_foo], Cherry.reactor_callbacks
|
17
|
+
assert_equal ActiveRecord::Base::CALLBACKS + [:before_peel, :after_peel], Fruit.reactor_callbacks
|
18
|
+
end
|
19
|
+
|
12
20
|
test 'Callbacks are invoked' do
|
13
21
|
b = Banana.create(color: 'blue')
|
14
22
|
b.peel!
|
@@ -25,5 +33,5 @@ class ReactorTest < ActiveSupport::TestCase
|
|
25
33
|
end
|
26
34
|
assert_empty FruitReactor.calls
|
27
35
|
assert_equal false, FruitReactor.scrammed?
|
28
|
-
end
|
36
|
+
end
|
29
37
|
end
|
data/test/models/cherry.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: activerecord-reactor
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.2
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-02-
|
12
|
+
date: 2013-02-23 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: activerecord
|
16
|
-
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirement: &2151926240 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ~>
|
@@ -21,15 +21,10 @@ dependencies:
|
|
21
21
|
version: 3.2.3
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
|
-
version_requirements:
|
25
|
-
none: false
|
26
|
-
requirements:
|
27
|
-
- - ~>
|
28
|
-
- !ruby/object:Gem::Version
|
29
|
-
version: 3.2.3
|
24
|
+
version_requirements: *2151926240
|
30
25
|
- !ruby/object:Gem::Dependency
|
31
26
|
name: sqlite3
|
32
|
-
requirement: !ruby/object:Gem::Requirement
|
27
|
+
requirement: &2151925700 !ruby/object:Gem::Requirement
|
33
28
|
none: false
|
34
29
|
requirements:
|
35
30
|
- - ! '>='
|
@@ -37,15 +32,10 @@ dependencies:
|
|
37
32
|
version: '0'
|
38
33
|
type: :development
|
39
34
|
prerelease: false
|
40
|
-
version_requirements:
|
41
|
-
none: false
|
42
|
-
requirements:
|
43
|
-
- - ! '>='
|
44
|
-
- !ruby/object:Gem::Version
|
45
|
-
version: '0'
|
35
|
+
version_requirements: *2151925700
|
46
36
|
- !ruby/object:Gem::Dependency
|
47
37
|
name: simplecov
|
48
|
-
requirement: !ruby/object:Gem::Requirement
|
38
|
+
requirement: &2151925080 !ruby/object:Gem::Requirement
|
49
39
|
none: false
|
50
40
|
requirements:
|
51
41
|
- - ! '>='
|
@@ -53,15 +43,10 @@ dependencies:
|
|
53
43
|
version: '0'
|
54
44
|
type: :development
|
55
45
|
prerelease: false
|
56
|
-
version_requirements:
|
57
|
-
none: false
|
58
|
-
requirements:
|
59
|
-
- - ! '>='
|
60
|
-
- !ruby/object:Gem::Version
|
61
|
-
version: '0'
|
46
|
+
version_requirements: *2151925080
|
62
47
|
- !ruby/object:Gem::Dependency
|
63
48
|
name: rake
|
64
|
-
requirement: !ruby/object:Gem::Requirement
|
49
|
+
requirement: &2151924320 !ruby/object:Gem::Requirement
|
65
50
|
none: false
|
66
51
|
requirements:
|
67
52
|
- - ! '>='
|
@@ -69,15 +54,10 @@ dependencies:
|
|
69
54
|
version: 0.8.7
|
70
55
|
type: :development
|
71
56
|
prerelease: false
|
72
|
-
version_requirements:
|
73
|
-
none: false
|
74
|
-
requirements:
|
75
|
-
- - ! '>='
|
76
|
-
- !ruby/object:Gem::Version
|
77
|
-
version: 0.8.7
|
57
|
+
version_requirements: *2151924320
|
78
58
|
- !ruby/object:Gem::Dependency
|
79
59
|
name: debugger
|
80
|
-
requirement: !ruby/object:Gem::Requirement
|
60
|
+
requirement: &2151923520 !ruby/object:Gem::Requirement
|
81
61
|
none: false
|
82
62
|
requirements:
|
83
63
|
- - ! '>='
|
@@ -85,35 +65,31 @@ dependencies:
|
|
85
65
|
version: '0'
|
86
66
|
type: :development
|
87
67
|
prerelease: false
|
88
|
-
version_requirements:
|
89
|
-
|
90
|
-
|
91
|
-
- - ! '>='
|
92
|
-
- !ruby/object:Gem::Version
|
93
|
-
version: '0'
|
94
|
-
description: ''
|
68
|
+
version_requirements: *2151923520
|
69
|
+
description: ActiveRecord Reactors provide a defined way to react on default or custom
|
70
|
+
Active Record callbacks. Observers without the magic, and without the hassle.
|
95
71
|
email: mtgrosser@gmx.net
|
96
72
|
executables: []
|
97
73
|
extensions: []
|
98
74
|
extra_rdoc_files: []
|
99
75
|
files:
|
100
|
-
- lib/active_record/active_record.rb
|
101
76
|
- lib/active_record/reactor/callbacks.rb
|
102
77
|
- lib/active_record/reactor/version.rb
|
103
78
|
- lib/active_record/reactor.rb
|
104
79
|
- lib/activerecord-reactor.rb
|
105
80
|
- MIT-LICENSE
|
106
81
|
- README.md
|
82
|
+
- CHANGELOG
|
107
83
|
- Rakefile
|
84
|
+
- test/cases/reactor_test.rb
|
108
85
|
- test/models/banana.rb
|
86
|
+
- test/models/cherry.rb
|
109
87
|
- test/models/fruit.rb
|
110
88
|
- test/models/fruit_reactor.rb
|
111
89
|
- test/models/peel_reactor.rb
|
112
|
-
- test/models/cherry.rb
|
113
|
-
- test/cases/reactor_test.rb
|
114
|
-
- test/test_helper.rb
|
115
|
-
- test/support/custom_assertions.rb
|
116
90
|
- test/schema.rb
|
91
|
+
- test/support/custom_assertions.rb
|
92
|
+
- test/test_helper.rb
|
117
93
|
homepage: https://github.com/mtgrosser/activerecord-reactor
|
118
94
|
licenses: []
|
119
95
|
post_install_message:
|
@@ -134,17 +110,17 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
134
110
|
version: '0'
|
135
111
|
requirements: []
|
136
112
|
rubyforge_project:
|
137
|
-
rubygems_version: 1.8.
|
113
|
+
rubygems_version: 1.8.11
|
138
114
|
signing_key:
|
139
115
|
specification_version: 3
|
140
|
-
summary:
|
116
|
+
summary: Controlled reactions on ActiveRecord callbacks
|
141
117
|
test_files:
|
118
|
+
- test/cases/reactor_test.rb
|
142
119
|
- test/models/banana.rb
|
120
|
+
- test/models/cherry.rb
|
143
121
|
- test/models/fruit.rb
|
144
122
|
- test/models/fruit_reactor.rb
|
145
123
|
- test/models/peel_reactor.rb
|
146
|
-
- test/models/cherry.rb
|
147
|
-
- test/cases/reactor_test.rb
|
148
|
-
- test/test_helper.rb
|
149
|
-
- test/support/custom_assertions.rb
|
150
124
|
- test/schema.rb
|
125
|
+
- test/support/custom_assertions.rb
|
126
|
+
- test/test_helper.rb
|