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,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