rails-observers 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. data/.gitignore +17 -0
  2. data/Gemfile +11 -0
  3. data/LICENSE +22 -0
  4. data/README.md +102 -0
  5. data/Rakefile +34 -0
  6. data/lib/generators/active_record/observer/observer_generator.rb +17 -0
  7. data/lib/generators/active_record/observer/templates/observer.rb +4 -0
  8. data/lib/generators/rails/observer/USAGE +12 -0
  9. data/lib/generators/rails/observer/observer_generator.rb +7 -0
  10. data/lib/generators/test_unit/observer/observer_generator.rb +15 -0
  11. data/lib/generators/test_unit/observer/templates/unit_test.rb +9 -0
  12. data/lib/rails-observers.rb +30 -0
  13. data/lib/rails/observers/action_controller/caching.rb +12 -0
  14. data/lib/rails/observers/action_controller/caching/sweeping.rb +113 -0
  15. data/lib/rails/observers/active_model/active_model.rb +4 -0
  16. data/lib/rails/observers/active_model/observer_array.rb +152 -0
  17. data/lib/rails/observers/active_model/observing.rb +374 -0
  18. data/lib/rails/observers/activerecord/active_record.rb +5 -0
  19. data/lib/rails/observers/activerecord/base.rb +8 -0
  20. data/lib/rails/observers/activerecord/observer.rb +125 -0
  21. data/lib/rails/observers/version.rb +5 -0
  22. data/rails-observers.gemspec +26 -0
  23. data/test/configuration_test.rb +37 -0
  24. data/test/console_test.rb +38 -0
  25. data/test/fixtures/developers.yml +4 -0
  26. data/test/fixtures/minimalistics.yml +2 -0
  27. data/test/fixtures/topics.yml +41 -0
  28. data/test/generators/generators_test_helper.rb +16 -0
  29. data/test/generators/namespaced_generators_test.rb +34 -0
  30. data/test/generators/observer_generator_test.rb +33 -0
  31. data/test/helper.rb +74 -0
  32. data/test/isolation/abstract_unit.rb +108 -0
  33. data/test/lifecycle_test.rb +249 -0
  34. data/test/models/observers.rb +27 -0
  35. data/test/observer_array_test.rb +222 -0
  36. data/test/observing_test.rb +183 -0
  37. data/test/rake_test.rb +40 -0
  38. data/test/sweeper_test.rb +83 -0
  39. data/test/transaction_callbacks_test.rb +278 -0
  40. metadata +216 -0
@@ -0,0 +1,27 @@
1
+ class ORM
2
+ include ActiveModel::Observing
3
+
4
+ def save
5
+ notify_observers :before_save
6
+ end
7
+
8
+ class Observer < ActiveModel::Observer
9
+ def before_save_invocations
10
+ @before_save_invocations ||= []
11
+ end
12
+
13
+ def before_save(record)
14
+ before_save_invocations << record
15
+ end
16
+ end
17
+ end
18
+
19
+ class Widget < ORM; end
20
+ class Budget < ORM; end
21
+ class WidgetObserver < ORM::Observer; end
22
+ class BudgetObserver < ORM::Observer; end
23
+ class AuditTrail < ORM::Observer
24
+ observe :widget, :budget
25
+ end
26
+
27
+ ORM.instantiate_observers
@@ -0,0 +1,222 @@
1
+ require 'minitest/autorun'
2
+ require 'active_model'
3
+ require 'rails/observers/active_model/active_model'
4
+ require 'models/observers'
5
+
6
+ class ObserverArrayTest < ActiveModel::TestCase
7
+ def teardown
8
+ ORM.observers.enable :all
9
+ Budget.observers.enable :all
10
+ Widget.observers.enable :all
11
+ end
12
+
13
+ def assert_observer_notified(model_class, observer_class)
14
+ observer_class.instance.before_save_invocations.clear
15
+ model_instance = model_class.new
16
+ model_instance.save
17
+ assert_equal [model_instance], observer_class.instance.before_save_invocations
18
+ end
19
+
20
+ def assert_observer_not_notified(model_class, observer_class)
21
+ observer_class.instance.before_save_invocations.clear
22
+ model_instance = model_class.new
23
+ model_instance.save
24
+ assert_equal [], observer_class.instance.before_save_invocations
25
+ end
26
+
27
+ test "all observers are enabled by default" do
28
+ assert_observer_notified Widget, WidgetObserver
29
+ assert_observer_notified Budget, BudgetObserver
30
+ assert_observer_notified Widget, AuditTrail
31
+ assert_observer_notified Budget, AuditTrail
32
+ end
33
+
34
+ test "can disable individual observers using a class constant" do
35
+ ORM.observers.disable WidgetObserver
36
+
37
+ assert_observer_not_notified Widget, WidgetObserver
38
+ assert_observer_notified Budget, BudgetObserver
39
+ assert_observer_notified Widget, AuditTrail
40
+ assert_observer_notified Budget, AuditTrail
41
+ end
42
+
43
+ test "can enable individual observers using a class constant" do
44
+ ORM.observers.disable :all
45
+ ORM.observers.enable AuditTrail
46
+
47
+ assert_observer_not_notified Widget, WidgetObserver
48
+ assert_observer_not_notified Budget, BudgetObserver
49
+ assert_observer_notified Widget, AuditTrail
50
+ assert_observer_notified Budget, AuditTrail
51
+ end
52
+
53
+ test "can disable individual observers using a symbol" do
54
+ ORM.observers.disable :budget_observer
55
+
56
+ assert_observer_notified Widget, WidgetObserver
57
+ assert_observer_not_notified Budget, BudgetObserver
58
+ assert_observer_notified Widget, AuditTrail
59
+ assert_observer_notified Budget, AuditTrail
60
+ end
61
+
62
+ test "can enable individual observers using a symbol" do
63
+ ORM.observers.disable :all
64
+ ORM.observers.enable :audit_trail
65
+
66
+ assert_observer_not_notified Widget, WidgetObserver
67
+ assert_observer_not_notified Budget, BudgetObserver
68
+ assert_observer_notified Widget, AuditTrail
69
+ assert_observer_notified Budget, AuditTrail
70
+ end
71
+
72
+ test "can disable multiple observers at a time" do
73
+ ORM.observers.disable :widget_observer, :budget_observer
74
+
75
+ assert_observer_not_notified Widget, WidgetObserver
76
+ assert_observer_not_notified Budget, BudgetObserver
77
+ assert_observer_notified Widget, AuditTrail
78
+ assert_observer_notified Budget, AuditTrail
79
+ end
80
+
81
+ test "can enable multiple observers at a time" do
82
+ ORM.observers.disable :all
83
+ ORM.observers.enable :widget_observer, :budget_observer
84
+
85
+ assert_observer_notified Widget, WidgetObserver
86
+ assert_observer_notified Budget, BudgetObserver
87
+ assert_observer_not_notified Widget, AuditTrail
88
+ assert_observer_not_notified Budget, AuditTrail
89
+ end
90
+
91
+ test "can disable all observers using :all" do
92
+ ORM.observers.disable :all
93
+
94
+ assert_observer_not_notified Widget, WidgetObserver
95
+ assert_observer_not_notified Budget, BudgetObserver
96
+ assert_observer_not_notified Widget, AuditTrail
97
+ assert_observer_not_notified Budget, AuditTrail
98
+ end
99
+
100
+ test "can enable all observers using :all" do
101
+ ORM.observers.disable :all
102
+ ORM.observers.enable :all
103
+
104
+ assert_observer_notified Widget, WidgetObserver
105
+ assert_observer_notified Budget, BudgetObserver
106
+ assert_observer_notified Widget, AuditTrail
107
+ assert_observer_notified Budget, AuditTrail
108
+ end
109
+
110
+ test "can disable observers on individual models without affecting those observers on other models" do
111
+ Widget.observers.disable :all
112
+
113
+ assert_observer_not_notified Widget, WidgetObserver
114
+ assert_observer_notified Budget, BudgetObserver
115
+ assert_observer_not_notified Widget, AuditTrail
116
+ assert_observer_notified Budget, AuditTrail
117
+ end
118
+
119
+ test "can enable observers on individual models without affecting those observers on other models" do
120
+ ORM.observers.disable :all
121
+ Budget.observers.enable AuditTrail
122
+
123
+ assert_observer_not_notified Widget, WidgetObserver
124
+ assert_observer_not_notified Budget, BudgetObserver
125
+ assert_observer_not_notified Widget, AuditTrail
126
+ assert_observer_notified Budget, AuditTrail
127
+ end
128
+
129
+ test "can disable observers for the duration of a block" do
130
+ yielded = false
131
+ ORM.observers.disable :budget_observer do
132
+ yielded = true
133
+ assert_observer_notified Widget, WidgetObserver
134
+ assert_observer_not_notified Budget, BudgetObserver
135
+ assert_observer_notified Widget, AuditTrail
136
+ assert_observer_notified Budget, AuditTrail
137
+ end
138
+
139
+ assert yielded
140
+ assert_observer_notified Widget, WidgetObserver
141
+ assert_observer_notified Budget, BudgetObserver
142
+ assert_observer_notified Widget, AuditTrail
143
+ assert_observer_notified Budget, AuditTrail
144
+ end
145
+
146
+ test "can enable observers for the duration of a block" do
147
+ yielded = false
148
+ Widget.observers.disable :all
149
+
150
+ Widget.observers.enable :all do
151
+ yielded = true
152
+ assert_observer_notified Widget, WidgetObserver
153
+ assert_observer_notified Budget, BudgetObserver
154
+ assert_observer_notified Widget, AuditTrail
155
+ assert_observer_notified Budget, AuditTrail
156
+ end
157
+
158
+ assert yielded
159
+ assert_observer_not_notified Widget, WidgetObserver
160
+ assert_observer_notified Budget, BudgetObserver
161
+ assert_observer_not_notified Widget, AuditTrail
162
+ assert_observer_notified Budget, AuditTrail
163
+ end
164
+
165
+ test "raises an appropriate error when a developer accidentally enables or disables the wrong class (i.e. Widget instead of WidgetObserver)" do
166
+ assert_raise ArgumentError do
167
+ ORM.observers.enable :widget
168
+ end
169
+
170
+ assert_raise ArgumentError do
171
+ ORM.observers.enable Widget
172
+ end
173
+
174
+ assert_raise ArgumentError do
175
+ ORM.observers.disable :widget
176
+ end
177
+
178
+ assert_raise ArgumentError do
179
+ ORM.observers.disable Widget
180
+ end
181
+ end
182
+
183
+ test "allows #enable at the superclass level to override #disable at the subclass level when called last" do
184
+ Widget.observers.disable :all
185
+ ORM.observers.enable :all
186
+
187
+ assert_observer_notified Widget, WidgetObserver
188
+ assert_observer_notified Budget, BudgetObserver
189
+ assert_observer_notified Widget, AuditTrail
190
+ assert_observer_notified Budget, AuditTrail
191
+ end
192
+
193
+ test "allows #disable at the superclass level to override #enable at the subclass level when called last" do
194
+ Budget.observers.enable :audit_trail
195
+ ORM.observers.disable :audit_trail
196
+
197
+ assert_observer_notified Widget, WidgetObserver
198
+ assert_observer_notified Budget, BudgetObserver
199
+ assert_observer_not_notified Widget, AuditTrail
200
+ assert_observer_not_notified Budget, AuditTrail
201
+ end
202
+
203
+ test "can use the block form at different levels of the hierarchy" do
204
+ yielded = false
205
+ Widget.observers.disable :all
206
+
207
+ ORM.observers.enable :all do
208
+ yielded = true
209
+ assert_observer_notified Widget, WidgetObserver
210
+ assert_observer_notified Budget, BudgetObserver
211
+ assert_observer_notified Widget, AuditTrail
212
+ assert_observer_notified Budget, AuditTrail
213
+ end
214
+
215
+ assert yielded
216
+ assert_observer_not_notified Widget, WidgetObserver
217
+ assert_observer_notified Budget, BudgetObserver
218
+ assert_observer_not_notified Widget, AuditTrail
219
+ assert_observer_notified Budget, AuditTrail
220
+ end
221
+ end
222
+
@@ -0,0 +1,183 @@
1
+ require 'minitest/autorun'
2
+ require 'active_model'
3
+ require 'rails/observers/active_model/active_model'
4
+
5
+ class ObservedModel
6
+ include ActiveModel::Observing
7
+
8
+ class Observer
9
+ end
10
+ end
11
+
12
+ class FooObserver < ActiveModel::Observer
13
+ class << self
14
+ public :new
15
+ end
16
+
17
+ attr_accessor :stub
18
+
19
+ def on_spec(record, *args)
20
+ stub.event_with(record, *args) if stub
21
+ end
22
+
23
+ def around_save(record)
24
+ yield :in_around_save
25
+ end
26
+ end
27
+
28
+ class Foo
29
+ include ActiveModel::Observing
30
+ end
31
+
32
+ class ObservingTest < ActiveModel::TestCase
33
+ def setup
34
+ ObservedModel.observers.clear
35
+ end
36
+
37
+ test "initializes model with no cached observers" do
38
+ assert ObservedModel.observers.empty?, "Not empty: #{ObservedModel.observers.inspect}"
39
+ end
40
+
41
+ test "stores cached observers in an array" do
42
+ ObservedModel.observers << :foo
43
+ assert ObservedModel.observers.include?(:foo), ":foo not in #{ObservedModel.observers.inspect}"
44
+ end
45
+
46
+ test "flattens array of assigned cached observers" do
47
+ ObservedModel.observers = [[:foo], :bar]
48
+ assert ObservedModel.observers.include?(:foo), ":foo not in #{ObservedModel.observers.inspect}"
49
+ assert ObservedModel.observers.include?(:bar), ":bar not in #{ObservedModel.observers.inspect}"
50
+ end
51
+
52
+ test "uses an ObserverArray so observers can be disabled" do
53
+ ObservedModel.observers = [:foo, :bar]
54
+ assert ObservedModel.observers.is_a?(ActiveModel::ObserverArray)
55
+ end
56
+
57
+ test "instantiates observer names passed as strings" do
58
+ ObservedModel.observers << 'foo_observer'
59
+ FooObserver.expects(:instance)
60
+ ObservedModel.instantiate_observers
61
+ end
62
+
63
+ test "instantiates observer names passed as symbols" do
64
+ ObservedModel.observers << :foo_observer
65
+ FooObserver.expects(:instance)
66
+ ObservedModel.instantiate_observers
67
+ end
68
+
69
+ test "instantiates observer classes" do
70
+ ObservedModel.observers << ObservedModel::Observer
71
+ ObservedModel::Observer.expects(:instance)
72
+ ObservedModel.instantiate_observers
73
+ end
74
+
75
+ test "raises an appropriate error when a developer accidentally adds the wrong class (i.e. Widget instead of WidgetObserver)" do
76
+ assert_raise ArgumentError do
77
+ ObservedModel.observers = ['string']
78
+ ObservedModel.instantiate_observers
79
+ end
80
+ assert_raise ArgumentError do
81
+ ObservedModel.observers = [:string]
82
+ ObservedModel.instantiate_observers
83
+ end
84
+ assert_raise ArgumentError do
85
+ ObservedModel.observers = [String]
86
+ ObservedModel.instantiate_observers
87
+ end
88
+ end
89
+
90
+ test "passes observers to subclasses" do
91
+ FooObserver.instance
92
+ bar = Class.new(Foo)
93
+ assert_equal Foo.observers_count, bar.observers_count
94
+ end
95
+ end
96
+
97
+ class ObserverTest < ActiveModel::TestCase
98
+ def setup
99
+ ObservedModel.observers = :foo_observer
100
+ FooObserver.singleton_class.instance_eval do
101
+ alias_method :original_observed_classes, :observed_classes
102
+ end
103
+ end
104
+
105
+ def teardown
106
+ FooObserver.singleton_class.instance_eval do
107
+ undef_method :observed_classes
108
+ alias_method :observed_classes, :original_observed_classes
109
+ end
110
+ end
111
+
112
+ test "guesses implicit observable model name" do
113
+ assert_equal Foo, FooObserver.observed_class
114
+ end
115
+
116
+ test "tracks implicit observable models" do
117
+ instance = FooObserver.new
118
+ assert_equal [Foo], instance.observed_classes
119
+ end
120
+
121
+ test "tracks explicit observed model class" do
122
+ FooObserver.observe ObservedModel
123
+ instance = FooObserver.new
124
+ assert_equal [ObservedModel], instance.observed_classes
125
+ end
126
+
127
+ test "tracks explicit observed model as string" do
128
+ FooObserver.observe 'observed_model'
129
+ instance = FooObserver.new
130
+ assert_equal [ObservedModel], instance.observed_classes
131
+ end
132
+
133
+ test "tracks explicit observed model as symbol" do
134
+ FooObserver.observe :observed_model
135
+ instance = FooObserver.new
136
+ assert_equal [ObservedModel], instance.observed_classes
137
+ end
138
+
139
+ test "calls existing observer event" do
140
+ foo = Foo.new
141
+ FooObserver.instance.stub = stub
142
+ FooObserver.instance.stub.expects(:event_with).with(foo)
143
+ Foo.notify_observers(:on_spec, foo)
144
+ end
145
+
146
+ test "calls existing observer event from the instance" do
147
+ foo = Foo.new
148
+ FooObserver.instance.stub = stub
149
+ FooObserver.instance.stub.expects(:event_with).with(foo)
150
+ foo.notify_observers(:on_spec)
151
+ end
152
+
153
+ test "passes extra arguments" do
154
+ foo = Foo.new
155
+ FooObserver.instance.stub = stub
156
+ FooObserver.instance.stub.expects(:event_with).with(foo, :bar)
157
+ Foo.send(:notify_observers, :on_spec, foo, :bar)
158
+ end
159
+
160
+ test "skips nonexistent observer event" do
161
+ foo = Foo.new
162
+ Foo.notify_observers(:whatever, foo)
163
+ end
164
+
165
+ test "update passes a block on to the observer" do
166
+ yielded_value = nil
167
+ FooObserver.instance.update(:around_save, Foo.new) do |val|
168
+ yielded_value = val
169
+ end
170
+ assert_equal :in_around_save, yielded_value
171
+ end
172
+
173
+ test "observe redefines observed_classes class method" do
174
+ class BarObserver < ActiveModel::Observer
175
+ observe :foo
176
+ end
177
+
178
+ assert_equal [Foo], BarObserver.observed_classes
179
+
180
+ BarObserver.observe(ObservedModel)
181
+ assert_equal [ObservedModel], BarObserver.observed_classes
182
+ end
183
+ end
@@ -0,0 +1,40 @@
1
+ require 'isolation/abstract_unit'
2
+ require 'rails-observers'
3
+
4
+ module ApplicationTests
5
+ class RakeTest < ActiveSupport::TestCase
6
+ include ActiveSupport::Testing::Isolation
7
+
8
+ def setup
9
+ build_app
10
+ boot_rails
11
+ FileUtils.rm_rf("#{app_path}/config/environments")
12
+ end
13
+
14
+ def teardown
15
+ teardown_app
16
+ end
17
+
18
+ def test_load_activerecord_base_when_we_use_observers
19
+ Dir.chdir(app_path) do
20
+ `bundle exec rails g model user;
21
+ bundle exec rake db:migrate;
22
+ bundle exec rails g observer user;`
23
+
24
+ add_to_config "config.active_record.observers = :user_observer"
25
+
26
+ assert_equal "0", `bundle exec rails r "puts User.count"`.strip
27
+
28
+ app_file "lib/tasks/count_user.rake", <<-RUBY
29
+ namespace :user do
30
+ task :count => :environment do
31
+ puts User.count
32
+ end
33
+ end
34
+ RUBY
35
+
36
+ assert_equal "0", `bundle exec rake user:count`.strip
37
+ end
38
+ end
39
+ end
40
+ end