rails-observers 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.
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