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.
- data/.gitignore +17 -0
- data/Gemfile +11 -0
- data/LICENSE +22 -0
- data/README.md +102 -0
- data/Rakefile +34 -0
- data/lib/generators/active_record/observer/observer_generator.rb +17 -0
- data/lib/generators/active_record/observer/templates/observer.rb +4 -0
- data/lib/generators/rails/observer/USAGE +12 -0
- data/lib/generators/rails/observer/observer_generator.rb +7 -0
- data/lib/generators/test_unit/observer/observer_generator.rb +15 -0
- data/lib/generators/test_unit/observer/templates/unit_test.rb +9 -0
- data/lib/rails-observers.rb +30 -0
- data/lib/rails/observers/action_controller/caching.rb +12 -0
- data/lib/rails/observers/action_controller/caching/sweeping.rb +113 -0
- data/lib/rails/observers/active_model/active_model.rb +4 -0
- data/lib/rails/observers/active_model/observer_array.rb +152 -0
- data/lib/rails/observers/active_model/observing.rb +374 -0
- data/lib/rails/observers/activerecord/active_record.rb +5 -0
- data/lib/rails/observers/activerecord/base.rb +8 -0
- data/lib/rails/observers/activerecord/observer.rb +125 -0
- data/lib/rails/observers/version.rb +5 -0
- data/rails-observers.gemspec +26 -0
- data/test/configuration_test.rb +37 -0
- data/test/console_test.rb +38 -0
- data/test/fixtures/developers.yml +4 -0
- data/test/fixtures/minimalistics.yml +2 -0
- data/test/fixtures/topics.yml +41 -0
- data/test/generators/generators_test_helper.rb +16 -0
- data/test/generators/namespaced_generators_test.rb +34 -0
- data/test/generators/observer_generator_test.rb +33 -0
- data/test/helper.rb +74 -0
- data/test/isolation/abstract_unit.rb +108 -0
- data/test/lifecycle_test.rb +249 -0
- data/test/models/observers.rb +27 -0
- data/test/observer_array_test.rb +222 -0
- data/test/observing_test.rb +183 -0
- data/test/rake_test.rb +40 -0
- data/test/sweeper_test.rb +83 -0
- data/test/transaction_callbacks_test.rb +278 -0
- metadata +216 -0
@@ -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,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
|
data/test/helper.rb
ADDED
@@ -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
|