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,5 @@
1
+ module Rails
2
+ module Observers
3
+ VERSION = "0.1.0"
4
+ end
5
+ end
@@ -0,0 +1,26 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/rails/observers/version', __FILE__)
3
+
4
+ Gem::Specification.new do |s|
5
+ s.name = "rails-observers"
6
+ s.authors = ["Rafael Mendonça França", "Steve Klabnik"]
7
+ s.email = ["rafaelmfranca@gmail.com", "steve@steveklabnik.com"]
8
+ s.description = %q{Rails observer (removed from core in Rails 4.0)}
9
+ s.summary = %q{ActiveModel::Observer, ActiveRecord::Observer and ActionController::Caching::Sweeper extracted from Rails.}
10
+ s.homepage = "https://github.com/rails/rails-observers"
11
+ s.version = Rails::Observers::VERSION
12
+
13
+ s.files = [".gitignore","Gemfile","LICENSE","README.md","Rakefile","lib/generators/active_record/observer/observer_generator.rb","lib/generators/active_record/observer/templates/observer.rb","lib/generators/rails/observer/USAGE","lib/generators/rails/observer/observer_generator.rb","lib/generators/test_unit/observer/observer_generator.rb","lib/generators/test_unit/observer/templates/unit_test.rb","lib/rails-observers.rb","lib/rails/observers/action_controller/caching.rb","lib/rails/observers/action_controller/caching/sweeping.rb","lib/rails/observers/active_model/active_model.rb","lib/rails/observers/active_model/observer_array.rb","lib/rails/observers/active_model/observing.rb","lib/rails/observers/activerecord/active_record.rb","lib/rails/observers/activerecord/base.rb","lib/rails/observers/activerecord/observer.rb","lib/rails/observers/version.rb","rails-observers.gemspec","test/configuration_test.rb","test/console_test.rb","test/fixtures/developers.yml","test/fixtures/minimalistics.yml","test/fixtures/topics.yml","test/generators/generators_test_helper.rb","test/generators/namespaced_generators_test.rb","test/generators/observer_generator_test.rb","test/helper.rb","test/isolation/abstract_unit.rb","test/lifecycle_test.rb","test/models/observers.rb","test/observer_array_test.rb","test/observing_test.rb","test/rake_test.rb","test/sweeper_test.rb","test/transaction_callbacks_test.rb"]
14
+ s.test_files = ["test/configuration_test.rb","test/console_test.rb","test/fixtures/developers.yml","test/fixtures/minimalistics.yml","test/fixtures/topics.yml","test/generators/generators_test_helper.rb","test/generators/namespaced_generators_test.rb","test/generators/observer_generator_test.rb","test/helper.rb","test/isolation/abstract_unit.rb","test/lifecycle_test.rb","test/models/observers.rb","test/observer_array_test.rb","test/observing_test.rb","test/rake_test.rb","test/sweeper_test.rb","test/transaction_callbacks_test.rb"]
15
+ s.executables = []
16
+ s.require_paths = ["lib"]
17
+
18
+ s.add_dependency 'railties', '~> 4.0.0.beta'
19
+
20
+ s.add_development_dependency 'minitest', '>= 3'
21
+ s.add_development_dependency 'activerecord', '~> 4.0.0.beta'
22
+ s.add_development_dependency 'activemodel', '~> 4.0.0.beta'
23
+ s.add_development_dependency 'actionmailer', '~> 4.0.0.beta'
24
+ s.add_development_dependency 'actionpack', '~> 4.0.0.beta'
25
+ s.add_development_dependency 'sqlite3', '~> 1.3'
26
+ end
@@ -0,0 +1,37 @@
1
+ require 'isolation/abstract_unit'
2
+ require 'rails-observers'
3
+
4
+ class ConfigurationTest < ActiveSupport::TestCase
5
+ include ActiveSupport::Testing::Isolation
6
+
7
+ def setup
8
+ build_app
9
+ boot_rails
10
+ FileUtils.rm_rf("#{app_path}/config/environments")
11
+ end
12
+
13
+ def teardown
14
+ teardown_app
15
+ end
16
+
17
+ test "config.active_record.observers" do
18
+ add_to_config <<-RUBY
19
+ config.active_record.observers = :foo_observer
20
+ RUBY
21
+
22
+ app_file 'app/models/foo.rb', <<-RUBY
23
+ class Foo < ActiveRecord::Base
24
+ end
25
+ RUBY
26
+
27
+ app_file 'app/models/foo_observer.rb', <<-RUBY
28
+ class FooObserver < ActiveRecord::Observer
29
+ end
30
+ RUBY
31
+
32
+ require "#{app_path}/config/environment"
33
+
34
+ _ = ActiveRecord::Base
35
+ assert defined?(FooObserver)
36
+ end
37
+ end
@@ -0,0 +1,38 @@
1
+ require 'isolation/abstract_unit'
2
+ require 'rails-observers'
3
+
4
+ class ConsoleTest < ActiveSupport::TestCase
5
+ include ActiveSupport::Testing::Isolation
6
+
7
+ def setup
8
+ build_app
9
+ boot_rails
10
+ end
11
+
12
+ def teardown
13
+ teardown_app
14
+ end
15
+
16
+ def load_environment
17
+ require "#{rails_root}/config/environment"
18
+ Rails.application.sandbox = false
19
+ Rails.application.load_console
20
+ end
21
+
22
+ def test_active_record_does_not_panic_when_referencing_an_observed_constant
23
+ add_to_config "config.active_record.observers = :user_observer"
24
+
25
+ app_file "app/models/user.rb", <<-MODEL
26
+ class User < ActiveRecord::Base
27
+ end
28
+ MODEL
29
+
30
+ app_file "app/models/user_observer.rb", <<-MODEL
31
+ class UserObserver < ActiveRecord::Observer
32
+ end
33
+ MODEL
34
+
35
+ load_environment
36
+ assert_nothing_raised { User }
37
+ end
38
+ end
@@ -0,0 +1,4 @@
1
+ david:
2
+ id: 1
3
+ name: David
4
+ salary: 80000
@@ -0,0 +1,2 @@
1
+ first:
2
+ id: 1
@@ -0,0 +1,41 @@
1
+ first:
2
+ id: 1
3
+ title: The First Topic
4
+ author_name: David
5
+ author_email_address: david@loudthinking.com
6
+ written_on: 2003-07-16t15:28:11.2233+01:00
7
+ last_read: 2004-04-15
8
+ bonus_time: 2005-01-30t15:28:00.00+01:00
9
+ content: Have a nice day
10
+ approved: false
11
+ replies_count: 1
12
+
13
+ second:
14
+ id: 2
15
+ title: The Second Topic of the day
16
+ author_name: Mary
17
+ written_on: 2004-07-15t15:28:00.0099+01:00
18
+ content: Have a nice day
19
+ approved: true
20
+ replies_count: 0
21
+ parent_id: 1
22
+ type: Reply
23
+
24
+ third:
25
+ id: 3
26
+ title: The Third Topic of the day
27
+ author_name: Carl
28
+ written_on: 2012-08-12t20:24:22.129346+00:00
29
+ content: I'm a troll
30
+ approved: true
31
+ replies_count: 1
32
+
33
+ fourth:
34
+ id: 4
35
+ title: The Fourth Topic of the day
36
+ author_name: Carl
37
+ written_on: 2006-07-15t15:28:00.0099+01:00
38
+ content: Why not?
39
+ approved: true
40
+ type: Reply
41
+ parent_id: 3
@@ -0,0 +1,16 @@
1
+ require 'minitest/autorun'
2
+
3
+ require 'fileutils'
4
+
5
+ require 'rails/all'
6
+ require 'rails/generators'
7
+ require 'rails/generators/test_case'
8
+
9
+ module TestApp
10
+ class Application < Rails::Application
11
+ end
12
+ end
13
+
14
+ # Call configure to load the settings from
15
+ # Rails.application.config.generators to Rails::Generators
16
+ Rails.application.load_generators
@@ -0,0 +1,34 @@
1
+ require 'generators/generators_test_helper'
2
+ require 'generators/rails/observer/observer_generator'
3
+
4
+ class NamespacedObserverGeneratorTest < Rails::Generators::TestCase
5
+ tests Rails::Generators::ObserverGenerator
6
+ arguments %w(account)
7
+ destination File.expand_path("../../tmp", __FILE__)
8
+
9
+ def setup
10
+ super
11
+ prepare_destination
12
+ Rails::Generators.namespace = TestApp
13
+ end
14
+
15
+ def teardown
16
+ super
17
+ Rails::Generators.namespace = nil
18
+ end
19
+
20
+ def test_invokes_default_orm
21
+ run_generator
22
+ assert_file "app/models/test_app/account_observer.rb", /module TestApp/, / class AccountObserver < ActiveRecord::Observer/
23
+ end
24
+
25
+ def test_invokes_default_orm_with_class_path
26
+ run_generator ["admin/account"]
27
+ assert_file "app/models/test_app/admin/account_observer.rb", /module TestApp/, / class Admin::AccountObserver < ActiveRecord::Observer/
28
+ end
29
+
30
+ def test_invokes_default_test_framework
31
+ run_generator
32
+ assert_file "test/unit/test_app/account_observer_test.rb", /module TestApp/, / class AccountObserverTest < ActiveSupport::TestCase/
33
+ end
34
+ end
@@ -0,0 +1,33 @@
1
+ require 'generators/generators_test_helper'
2
+ require 'generators/rails/observer/observer_generator'
3
+
4
+ class ObserverGeneratorTest < Rails::Generators::TestCase
5
+ tests Rails::Generators::ObserverGenerator
6
+ destination File.expand_path("../../tmp", __FILE__)
7
+ arguments %w(account)
8
+
9
+ def setup
10
+ super
11
+ prepare_destination
12
+ end
13
+
14
+ def test_invokes_default_orm
15
+ run_generator
16
+ assert_file "app/models/account_observer.rb", /class AccountObserver < ActiveRecord::Observer/
17
+ end
18
+
19
+ def test_invokes_default_orm_with_class_path
20
+ run_generator ["admin/account"]
21
+ assert_file "app/models/admin/account_observer.rb", /class Admin::AccountObserver < ActiveRecord::Observer/
22
+ end
23
+
24
+ def test_invokes_default_test_framework
25
+ run_generator
26
+ assert_file "test/unit/account_observer_test.rb", /class AccountObserverTest < ActiveSupport::TestCase/
27
+ end
28
+
29
+ def test_logs_if_the_test_framework_cannot_be_found
30
+ content = run_generator ["account", "--test-framework=rspec"]
31
+ assert_match(/rspec \[not found\]/, content)
32
+ end
33
+ end
@@ -0,0 +1,74 @@
1
+ require 'minitest/autorun'
2
+ require 'active_record'
3
+
4
+ require 'rails/observers/activerecord/active_record'
5
+
6
+ FIXTURES_ROOT = File.expand_path(File.dirname(__FILE__)) + "/fixtures"
7
+
8
+ class ActiveSupport::TestCase
9
+ include ActiveRecord::TestFixtures
10
+
11
+ self.fixture_path = FIXTURES_ROOT
12
+ self.use_instantiated_fixtures = false
13
+ self.use_transactional_fixtures = true
14
+ end
15
+
16
+ ActiveRecord::Base.configurations = { "test" => { adapter: 'sqlite3', database: ':memory:' } }
17
+ ActiveRecord::Base.establish_connection(:test)
18
+
19
+ ActiveRecord::Schema.verbose = false
20
+ ActiveRecord::Schema.define do
21
+ create_table :topics do |t|
22
+ t.string :title
23
+ t.string :author_name
24
+ t.string :author_email_address
25
+ t.datetime :written_on
26
+ t.time :bonus_time
27
+ t.date :last_read
28
+ t.text :content
29
+ t.text :important
30
+ t.boolean :approved, :default => true
31
+ t.integer :replies_count, :default => 0
32
+ t.integer :parent_id
33
+ t.string :parent_title
34
+ t.string :type
35
+ t.string :group
36
+ t.timestamps
37
+ end
38
+
39
+ create_table :comments do |t|
40
+ t.string :title
41
+ end
42
+
43
+ create_table :minimalistics do |t|
44
+ end
45
+
46
+ create_table :developers do |t|
47
+ t.string :name
48
+ t.integer :salary
49
+ end
50
+ end
51
+
52
+ class Topic < ActiveRecord::Base
53
+ has_many :replies, dependent: :destroy, foreign_key: "parent_id"
54
+ end
55
+
56
+ class Reply < Topic
57
+ belongs_to :topic, :foreign_key => "parent_id", :counter_cache => true
58
+ end
59
+
60
+ class Comment < ActiveRecord::Base
61
+ def self.lol
62
+ "lol"
63
+ end
64
+ end
65
+
66
+ class Developer < ActiveRecord::Base
67
+ end
68
+
69
+ class Minimalistic < ActiveRecord::Base
70
+ end
71
+
72
+ ActiveSupport::Deprecation.silence do
73
+ require 'active_record/test_case'
74
+ end
@@ -0,0 +1,108 @@
1
+ # Note:
2
+ # It is important to keep this file as light as possible
3
+ # the goal for tests that require this is to test booting up
4
+ # rails from an empty state, so anything added here could
5
+ # hide potential failures
6
+ #
7
+ # It is also good to know what is the bare minimum to get
8
+ # Rails booted up.
9
+ require 'fileutils'
10
+
11
+ require 'bundler/setup'
12
+ require 'minitest/autorun'
13
+ require 'active_support/test_case'
14
+
15
+ # These files do not require any others and are needed
16
+ # to run the tests
17
+ require "active_support/testing/isolation"
18
+ require "active_support/core_ext/kernel/reporting"
19
+ require 'tmpdir'
20
+
21
+ module TestHelpers
22
+ module Paths
23
+ def app_template_path
24
+ File.join Dir.tmpdir, 'app_template'
25
+ end
26
+
27
+ def tmp_path(*args)
28
+ @tmp_path ||= File.realpath(Dir.mktmpdir)
29
+ File.join(@tmp_path, *args)
30
+ end
31
+
32
+ def app_path(*args)
33
+ tmp_path(*%w[app] + args)
34
+ end
35
+
36
+ def rails_root
37
+ app_path
38
+ end
39
+ end
40
+
41
+ module Generation
42
+ # Build an application by invoking the generator and going through the whole stack.
43
+ def build_app(options = {})
44
+ @prev_rails_env = ENV['RAILS_ENV']
45
+ ENV['RAILS_ENV'] = 'development'
46
+
47
+ FileUtils.rm_rf(app_path)
48
+ FileUtils.cp_r(app_template_path, app_path)
49
+
50
+ # Delete the initializers unless requested
51
+ unless options[:initializers]
52
+ Dir["#{app_path}/config/initializers/*.rb"].each do |initializer|
53
+ File.delete(initializer)
54
+ end
55
+ end
56
+
57
+ unless options[:gemfile]
58
+ File.delete "#{app_path}/Gemfile"
59
+ end
60
+
61
+ routes = File.read("#{app_path}/config/routes.rb")
62
+ if routes =~ /(\n\s*end\s*)\Z/
63
+ File.open("#{app_path}/config/routes.rb", 'w') do |f|
64
+ f.puts $` + "\nmatch ':controller(/:action(/:id))(.:format)', :via => :all\n" + $1
65
+ end
66
+ end
67
+
68
+ add_to_config <<-RUBY
69
+ config.secret_token = "3b7cd727ee24e8444053437c36cc66c4"
70
+ config.session_store :cookie_store, :key => "_myapp_session"
71
+ config.active_support.deprecation = :log
72
+ config.action_controller.allow_forgery_protection = false
73
+ config.eager_load = false
74
+ RUBY
75
+ end
76
+
77
+ def teardown_app
78
+ ENV['RAILS_ENV'] = @prev_rails_env if @prev_rails_env
79
+ end
80
+
81
+ def add_to_config(str)
82
+ environment = File.read("#{app_path}/config/application.rb")
83
+ if environment =~ /(\n\s*end\s*end\s*)\Z/
84
+ File.open("#{app_path}/config/application.rb", 'w') do |f|
85
+ f.puts $` + "\n#{str}\n" + $1
86
+ end
87
+ end
88
+ end
89
+
90
+ def app_file(path, contents)
91
+ FileUtils.mkdir_p File.dirname("#{app_path}/#{path}")
92
+ File.open("#{app_path}/#{path}", 'w') do |f|
93
+ f.puts contents
94
+ end
95
+ end
96
+
97
+ def boot_rails
98
+ require 'rubygems' unless defined? Gem
99
+ require 'bundler'
100
+ Bundler.setup
101
+ end
102
+ end
103
+ end
104
+
105
+ class ActiveSupport::TestCase
106
+ include TestHelpers::Paths
107
+ include TestHelpers::Generation
108
+ end
@@ -0,0 +1,249 @@
1
+ require 'helper'
2
+
3
+ class SpecialDeveloper < Developer; end
4
+
5
+ class DeveloperObserver < ActiveRecord::Observer
6
+ def calls
7
+ @calls ||= []
8
+ end
9
+
10
+ def before_save(developer)
11
+ calls << developer
12
+ end
13
+ end
14
+
15
+ class SalaryChecker < ActiveRecord::Observer
16
+ observe :special_developer
17
+ attr_accessor :last_saved
18
+
19
+ def before_save(developer)
20
+ return developer.salary > 80000
21
+ end
22
+
23
+ module Implementation
24
+ def after_save(developer)
25
+ self.last_saved = developer
26
+ end
27
+ end
28
+ include Implementation
29
+ end
30
+
31
+ class TopicaAuditor < ActiveRecord::Observer
32
+ observe :topic
33
+
34
+ attr_reader :topic
35
+
36
+ def after_find(topic)
37
+ @topic = topic
38
+ end
39
+ end
40
+
41
+ class TopicObserver < ActiveRecord::Observer
42
+ attr_reader :topic
43
+
44
+ def after_find(topic)
45
+ @topic = topic
46
+ end
47
+
48
+ # Create an after_save callback, so a notify_observer hook is created
49
+ # on :topic.
50
+ def after_save(nothing)
51
+ end
52
+ end
53
+
54
+ class MinimalisticObserver < ActiveRecord::Observer
55
+ attr_reader :minimalistic
56
+
57
+ def after_find(minimalistic)
58
+ @minimalistic = minimalistic
59
+ end
60
+ end
61
+
62
+ class MultiObserver < ActiveRecord::Observer
63
+ attr_reader :record
64
+
65
+ def self.observed_class() [ Topic, Developer ] end
66
+
67
+ cattr_reader :last_inherited
68
+ @@last_inherited = nil
69
+
70
+ def observed_class_inherited_with_testing(subclass)
71
+ observed_class_inherited_without_testing(subclass)
72
+ @@last_inherited = subclass
73
+ end
74
+
75
+ alias_method_chain :observed_class_inherited, :testing
76
+
77
+ def after_find(record)
78
+ @record = record
79
+ end
80
+ end
81
+
82
+ class ValidatedComment < Comment
83
+ attr_accessor :callers
84
+
85
+ before_validation :record_callers
86
+
87
+ after_validation do
88
+ record_callers
89
+ end
90
+
91
+ def record_callers
92
+ callers << self.class if callers
93
+ end
94
+ end
95
+
96
+ class ValidatedCommentObserver < ActiveRecord::Observer
97
+ attr_accessor :callers
98
+
99
+ def after_validation(model)
100
+ callers << self.class if callers
101
+ end
102
+ end
103
+
104
+
105
+ class AroundTopic < Topic
106
+ end
107
+
108
+ class AroundTopicObserver < ActiveRecord::Observer
109
+ observe :around_topic
110
+ def topic_ids
111
+ @topic_ids ||= []
112
+ end
113
+
114
+ def around_save(topic)
115
+ topic_ids << topic.id
116
+ yield(topic)
117
+ topic_ids << topic.id
118
+ end
119
+ end
120
+
121
+ class LifecycleTest < ActiveRecord::TestCase
122
+ fixtures :topics, :developers, :minimalistics
123
+
124
+ def test_before_destroy
125
+ topic = Topic.find(1)
126
+ assert_difference 'Topic.count', -(1 + topic.replies.size) do
127
+ topic.destroy
128
+ end
129
+ end
130
+
131
+ def test_auto_observer
132
+ topic_observer = TopicaAuditor.instance
133
+ assert_nil TopicaAuditor.observed_class
134
+ assert_equal [Topic], TopicaAuditor.observed_classes.to_a
135
+
136
+ topic = Topic.find(1)
137
+ assert_equal topic.title, topic_observer.topic.title
138
+ end
139
+
140
+ def test_inferred_auto_observer
141
+ topic_observer = TopicObserver.instance
142
+ assert_equal Topic, TopicObserver.observed_class
143
+
144
+ topic = Topic.find(1)
145
+ assert_equal topic.title, topic_observer.topic.title
146
+ end
147
+
148
+ def test_observing_two_classes
149
+ multi_observer = MultiObserver.instance
150
+
151
+ topic = Topic.find(1)
152
+ assert_equal topic.title, multi_observer.record.title
153
+
154
+ developer = Developer.find(1)
155
+ assert_equal developer.name, multi_observer.record.name
156
+ end
157
+
158
+ def test_observing_subclasses
159
+ multi_observer = MultiObserver.instance
160
+
161
+ developer = SpecialDeveloper.find(1)
162
+ assert_equal developer.name, multi_observer.record.name
163
+
164
+ klass = Class.new(Developer)
165
+ assert_equal klass, multi_observer.last_inherited
166
+
167
+ developer = klass.find(1)
168
+ assert_equal developer.name, multi_observer.record.name
169
+ end
170
+
171
+ def test_after_find_can_be_observed_when_its_not_defined_on_the_model
172
+ observer = MinimalisticObserver.instance
173
+ assert_equal Minimalistic, MinimalisticObserver.observed_class
174
+
175
+ minimalistic = Minimalistic.find(1)
176
+ assert_equal minimalistic, observer.minimalistic
177
+ end
178
+
179
+ def test_after_find_can_be_observed_when_its_defined_on_the_model
180
+ observer = TopicObserver.instance
181
+ assert_equal Topic, TopicObserver.observed_class
182
+
183
+ topic = Topic.find(1)
184
+ assert_equal topic, observer.topic
185
+ end
186
+
187
+ def test_invalid_observer
188
+ assert_raise(ArgumentError) { Topic.observers = Object.new; Topic.instantiate_observers }
189
+ end
190
+
191
+ test "model callbacks fire before observers are notified" do
192
+ callers = []
193
+
194
+ comment = ValidatedComment.new
195
+ comment.callers = ValidatedCommentObserver.instance.callers = callers
196
+
197
+ comment.valid?
198
+ assert_equal [ValidatedComment, ValidatedComment, ValidatedCommentObserver], callers,
199
+ "model callbacks did not fire before observers were notified"
200
+ end
201
+
202
+ test "able to save developer" do
203
+ SalaryChecker.instance # activate
204
+ developer = SpecialDeveloper.new :name => 'Roger', :salary => 100000
205
+ assert developer.save, "developer with normal salary failed to save"
206
+ end
207
+
208
+ test "unable to save developer with low salary" do
209
+ SalaryChecker.instance # activate
210
+ developer = SpecialDeveloper.new :name => 'Rookie', :salary => 50000
211
+ assert !developer.save, "allowed to save a developer with too low salary"
212
+ end
213
+
214
+ test "able to call methods defined with included module" do # https://rails.lighthouseapp.com/projects/8994/tickets/6065-activerecordobserver-is-not-aware-of-method-added-by-including-modules
215
+ SalaryChecker.instance # activate
216
+ developer = SpecialDeveloper.create! :name => 'Roger', :salary => 100000
217
+ assert_equal developer, SalaryChecker.instance.last_saved
218
+ end
219
+
220
+ test "around filter from observer should accept block" do
221
+ observer = AroundTopicObserver.instance
222
+ topic = AroundTopic.new
223
+ topic.save
224
+ assert_nil observer.topic_ids.first
225
+ assert_not_nil observer.topic_ids.last
226
+ end
227
+
228
+ test "able to disable observers" do
229
+ observer = DeveloperObserver.instance # activate
230
+ observer.calls.clear
231
+
232
+ ActiveRecord::Base.observers.disable DeveloperObserver do
233
+ Developer.create! :name => 'Ancestor', :salary => 100000
234
+ SpecialDeveloper.create! :name => 'Descendent', :salary => 100000
235
+ end
236
+
237
+ assert_equal [], observer.calls
238
+ end
239
+
240
+ def test_observer_is_called_once
241
+ observer = DeveloperObserver.instance # activate
242
+ observer.calls.clear
243
+
244
+ developer = Developer.create! :name => 'Ancestor', :salary => 100000
245
+ special_developer = SpecialDeveloper.create! :name => 'Descendent', :salary => 100000
246
+
247
+ assert_equal [developer, special_developer], observer.calls
248
+ end
249
+ end