activerecord-reactor 1.0.0 → 1.0.2

Sign up to get free protection for your applications and to get access to all the features.
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
-