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 ADDED
@@ -0,0 +1,4 @@
1
+ 1.0.1
2
+
3
+ Remove incorrect class definitions
4
+ Update docs
data/README.md CHANGED
@@ -1,4 +1,79 @@
1
- activerecord-reactor
2
- ====================
1
+ [![Gem Version](https://badge.fury.io/rb/activerecord-reactor.png)](http://badge.fury.io/rb/activerecord-reactor) [![Code Climate](https://codeclimate.com/github/mtgrosser/activerecord-reactor.png)](https://codeclimate.com/github/mtgrosser/activerecord-reactor)
3
2
 
4
- Unobtrusive observers for Rails models
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 = 'ActiveModel::Reactor'
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,5 +1,5 @@
1
1
  module ActiveRecord
2
2
  class Reactor
3
- VERSION = '1.0.0'
3
+ VERSION = '1.0.2'
4
4
  end
5
5
  end
@@ -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
@@ -1,7 +1,7 @@
1
1
  require 'active_record'
2
- require 'active_record/reactor'
3
- require 'active_record/reactor/callbacks'
4
- require 'active_record/reactor/version'
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
@@ -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
@@ -1,3 +1,3 @@
1
1
  class Cherry < Fruit
2
-
2
+ define_reactor_callbacks :foo, only: :before
3
3
  end
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.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-22 00:00:00.000000000 Z
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: !ruby/object:Gem::Requirement
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: !ruby/object:Gem::Requirement
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: !ruby/object:Gem::Requirement
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: !ruby/object:Gem::Requirement
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: !ruby/object:Gem::Requirement
89
- none: false
90
- requirements:
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.23
113
+ rubygems_version: 1.8.11
138
114
  signing_key:
139
115
  specification_version: 3
140
- summary: Unobtrusive observers for Rails models
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
@@ -1,12 +0,0 @@
1
- require 'active_record'
2
-
3
- module ActiveRecord
4
- class Reactor < ActiveModel::Reactor
5
- end
6
-
7
- Base.class_eval do
8
- include ActiveModel::Reactor::Callbacks
9
- self.reactor_callbacks = Base::CALLBACKS.dup
10
- end
11
- end
12
-