Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
张重言
rails
提交
21e7b846
R
rails
项目概览
张重言
/
rails
通知
1
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
R
rails
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
提交
Issue看板
体验新版 GitCode,发现更多精彩内容 >>
提交
21e7b846
编写于
10月 12, 2009
作者:
J
Joshua Peek
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
Callbacks, DeprecatedCallbacks = NewCallbacks, Callbacks
上级
9bc8defe
变更
15
展开全部
隐藏空白更改
内联
并排
Showing
15 changed file
with
1263 addition
and
1457 deletion
+1263
-1457
actionpack/lib/abstract_controller/callbacks.rb
actionpack/lib/abstract_controller/callbacks.rb
+3
-5
actionpack/lib/action_dispatch/middleware/callbacks.rb
actionpack/lib/action_dispatch/middleware/callbacks.rb
+1
-1
activemodel/lib/active_model/validations.rb
activemodel/lib/active_model/validations.rb
+4
-6
activerecord/lib/active_record/associations/association_collection.rb
.../lib/active_record/associations/association_collection.rb
+1
-1
activerecord/lib/active_record/callbacks.rb
activerecord/lib/active_record/callbacks.rb
+2
-2
activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
...lib/active_record/connection_adapters/abstract_adapter.rb
+2
-2
activerecord/lib/active_record/validations.rb
activerecord/lib/active_record/validations.rb
+7
-7
activesupport/lib/active_support/autoload.rb
activesupport/lib/active_support/autoload.rb
+1
-1
activesupport/lib/active_support/callbacks.rb
activesupport/lib/active_support/callbacks.rb
+452
-170
activesupport/lib/active_support/deprecated_callbacks.rb
activesupport/lib/active_support/deprecated_callbacks.rb
+281
-0
activesupport/lib/active_support/new_callbacks.rb
activesupport/lib/active_support/new_callbacks.rb
+0
-563
activesupport/lib/active_support/testing/setup_and_teardown.rb
...esupport/lib/active_support/testing/setup_and_teardown.rb
+2
-4
activesupport/test/callback_inheritance_test.rb
activesupport/test/callback_inheritance_test.rb
+21
-21
activesupport/test/callbacks_test.rb
activesupport/test/callbacks_test.rb
+486
-146
activesupport/test/new_callbacks_test.rb
activesupport/test/new_callbacks_test.rb
+0
-528
未找到文件。
actionpack/lib/abstract_controller/callbacks.rb
浏览文件 @
21e7b846
require
"active_support/new_callbacks"
module
AbstractController
module
Callbacks
extend
ActiveSupport
::
Concern
# Uses ActiveSupport::
New
Callbacks as the base functionality. For
# Uses ActiveSupport::Callbacks as the base functionality. For
# more details on the whole callback system, read the documentation
# for ActiveSupport::
New
Callbacks.
include
ActiveSupport
::
New
Callbacks
# for ActiveSupport::Callbacks.
include
ActiveSupport
::
Callbacks
included
do
define_callbacks
:process_action
,
:terminator
=>
"response_body"
...
...
actionpack/lib/action_dispatch/middleware/callbacks.rb
浏览文件 @
21e7b846
module
ActionDispatch
class
Callbacks
include
ActiveSupport
::
New
Callbacks
include
ActiveSupport
::
Callbacks
define_callbacks
:call
,
:terminator
=>
"result == false"
,
:rescuable
=>
true
define_callbacks
:prepare
,
:scope
=>
:name
...
...
activemodel/lib/active_model/validations.rb
浏览文件 @
21e7b846
require
'active_support/core_ext/array/extract_options'
require
'active_support/core_ext/hash/keys'
require
'active_support/concern'
require
'active_support/callbacks'
module
ActiveModel
module
Validations
extend
ActiveSupport
::
Concern
include
ActiveSupport
::
New
Callbacks
include
ActiveSupport
::
Callbacks
included
do
define_callbacks
:validate
,
:scope
=>
:name
...
...
@@ -99,7 +97,7 @@ def valid?
def
invalid?
!
valid?
end
protected
# Hook method defining how an attribute value should be retieved. By default this is assumed
# to be an instance named after the attribute. Override this method in subclasses should you
...
...
@@ -110,9 +108,9 @@ def invalid?
# def initialize(data = {})
# @data = data
# end
#
#
# private
#
#
# def read_attribute_for_validation(key)
# @data[key]
# end
...
...
activerecord/lib/active_record/associations/association_collection.rb
浏览文件 @
21e7b846
...
...
@@ -476,7 +476,7 @@ def remove_records(*records)
def
callback
(
method
,
record
)
callbacks_for
(
method
).
each
do
|
callback
|
ActiveSupport
::
Callbacks
::
Callback
.
new
(
method
,
callback
,
record
).
call
(
@owner
,
record
)
ActiveSupport
::
Deprecated
Callbacks
::
Callback
.
new
(
method
,
callback
,
record
).
call
(
@owner
,
record
)
end
end
...
...
activerecord/lib/active_record/callbacks.rb
浏览文件 @
21e7b846
...
...
@@ -20,7 +20,7 @@ module ActiveRecord
# * (6) <tt>after_save</tt>
#
# That's a total of eight callbacks, which gives you immense power to react and prepare for each state in the
# Active Record lifecycle. The sequence for calling <tt>Base#save</tt> for an existing record is similar, except that each
# Active Record lifecycle. The sequence for calling <tt>Base#save</tt> for an existing record is similar, except that each
# <tt>_on_create</tt> callback is replaced by the corresponding <tt>_on_update</tt> callback.
#
# Examples:
...
...
@@ -210,7 +210,7 @@ module ActiveRecord
# instead of quietly returning +false+.
module
Callbacks
extend
ActiveSupport
::
Concern
include
ActiveSupport
::
New
Callbacks
include
ActiveSupport
::
Callbacks
CALLBACKS
=
[
:after_initialize
,
:after_find
,
:before_validation
,
:after_validation
,
...
...
activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
浏览文件 @
21e7b846
...
...
@@ -32,7 +32,7 @@ module ConnectionAdapters # :nodoc:
class
AbstractAdapter
include
Quoting
,
DatabaseStatements
,
SchemaStatements
include
QueryCache
include
ActiveSupport
::
Callbacks
include
ActiveSupport
::
Deprecated
Callbacks
define_callbacks
:checkout
,
:checkin
@@row_even
=
true
...
...
@@ -75,7 +75,7 @@ def supports_count_distinct?
def
supports_ddl_transactions?
false
end
# Does this adapter support savepoints? PostgreSQL and MySQL do, SQLite
# does not.
def
supports_savepoints?
...
...
activerecord/lib/active_record/validations.rb
浏览文件 @
21e7b846
...
...
@@ -59,15 +59,15 @@ def full_messages(options = {})
end
# Translates an error message in it's default scope (<tt>activerecord.errrors.messages</tt>).
# Error messages are first looked up in <tt>models.MODEL.attributes.ATTRIBUTE.MESSAGE</tt>, if it's not there,
# it's looked up in <tt>models.MODEL.MESSAGE</tt> and if that is not there it returns the translation of the
# default message (e.g. <tt>activerecord.errors.messages.MESSAGE</tt>). The translated model name,
# Error messages are first looked up in <tt>models.MODEL.attributes.ATTRIBUTE.MESSAGE</tt>, if it's not there,
# it's looked up in <tt>models.MODEL.MESSAGE</tt> and if that is not there it returns the translation of the
# default message (e.g. <tt>activerecord.errors.messages.MESSAGE</tt>). The translated model name,
# translated attribute name and the value are available for interpolation.
#
# When using inheritance in your models, it will check all the inherited models too, but only if the model itself
# hasn't been found. Say you have <tt>class Admin < User; end</tt> and you wanted the translation for the <tt>:blank</tt>
# error +message+ for the <tt>title</tt> +attribute+, it looks for these translations:
#
#
# <ol>
# <li><tt>activerecord.errors.models.admin.attributes.title.blank</tt></li>
# <li><tt>activerecord.errors.models.admin.blank</tt></li>
...
...
@@ -80,10 +80,10 @@ def generate_message(attribute, message = :invalid, options = {})
message
,
options
[
:default
]
=
options
[
:default
],
message
if
options
[
:default
].
is_a?
(
Symbol
)
defaults
=
@base
.
class
.
self_and_descendants_from_active_record
.
map
do
|
klass
|
[
:"models.
#{
klass
.
name
.
underscore
}
.attributes.
#{
attribute
}
.
#{
message
}
"
,
[
:"models.
#{
klass
.
name
.
underscore
}
.attributes.
#{
attribute
}
.
#{
message
}
"
,
:"models.
#{
klass
.
name
.
underscore
}
.
#{
message
}
"
]
end
defaults
<<
options
.
delete
(
:default
)
defaults
=
defaults
.
compact
.
flatten
<<
:"messages.
#{
message
}
"
...
...
@@ -104,7 +104,7 @@ def generate_message(attribute, message = :invalid, options = {})
module
Validations
extend
ActiveSupport
::
Concern
include
ActiveSupport
::
Callbacks
include
ActiveSupport
::
Deprecated
Callbacks
include
ActiveModel
::
Validations
included
do
...
...
activesupport/lib/active_support/autoload.rb
浏览文件 @
21e7b846
...
...
@@ -8,6 +8,7 @@ module ActiveSupport
autoload
:Concern
,
'active_support/concern'
autoload
:ConcurrentHash
,
'active_support/concurrent_hash'
autoload
:DependencyModule
,
'active_support/dependency_module'
autoload
:DeprecatedCallbacks
,
'active_support/deprecated_callbacks'
autoload
:Deprecation
,
'active_support/deprecation'
autoload
:Gzip
,
'active_support/gzip'
autoload
:Inflector
,
'active_support/inflector'
...
...
@@ -15,7 +16,6 @@ module ActiveSupport
autoload
:MessageEncryptor
,
'active_support/message_encryptor'
autoload
:MessageVerifier
,
'active_support/message_verifier'
autoload
:Multibyte
,
'active_support/multibyte'
autoload
:NewCallbacks
,
'active_support/new_callbacks'
autoload
:OptionMerger
,
'active_support/option_merger'
autoload
:Orchestra
,
'active_support/orchestra'
autoload
:OrderedHash
,
'active_support/ordered_hash'
...
...
activesupport/lib/active_support/callbacks.rb
浏览文件 @
21e7b846
此差异已折叠。
点击以展开。
activesupport/lib/active_support/deprecated_callbacks.rb
0 → 100644
浏览文件 @
21e7b846
require
'active_support/core_ext/array/extract_options'
module
ActiveSupport
# Callbacks are hooks into the lifecycle of an object that allow you to trigger logic
# before or after an alteration of the object state.
#
# Mixing in this module allows you to define callbacks in your class.
#
# Example:
# class Storage
# include ActiveSupport::DeprecatedCallbacks
#
# define_callbacks :before_save, :after_save
# end
#
# class ConfigStorage < Storage
# before_save :saving_message
# def saving_message
# puts "saving..."
# end
#
# after_save do |object|
# puts "saved"
# end
#
# def save
# run_callbacks(:before_save)
# puts "- save"
# run_callbacks(:after_save)
# end
# end
#
# config = ConfigStorage.new
# config.save
#
# Output:
# saving...
# - save
# saved
#
# Callbacks from parent classes are inherited.
#
# Example:
# class Storage
# include ActiveSupport::DeprecatedCallbacks
#
# define_callbacks :before_save, :after_save
#
# before_save :prepare
# def prepare
# puts "preparing save"
# end
# end
#
# class ConfigStorage < Storage
# before_save :saving_message
# def saving_message
# puts "saving..."
# end
#
# after_save do |object|
# puts "saved"
# end
#
# def save
# run_callbacks(:before_save)
# puts "- save"
# run_callbacks(:after_save)
# end
# end
#
# config = ConfigStorage.new
# config.save
#
# Output:
# preparing save
# saving...
# - save
# saved
module
DeprecatedCallbacks
class
CallbackChain
<
Array
def
self
.
build
(
kind
,
*
methods
,
&
block
)
methods
,
options
=
extract_options
(
*
methods
,
&
block
)
methods
.
map!
{
|
method
|
Callback
.
new
(
kind
,
method
,
options
)
}
new
(
methods
)
end
def
run
(
object
,
options
=
{},
&
terminator
)
enumerator
=
options
[
:enumerator
]
||
:each
unless
block_given?
send
(
enumerator
)
{
|
callback
|
callback
.
call
(
object
)
}
else
send
(
enumerator
)
do
|
callback
|
result
=
callback
.
call
(
object
)
break
result
if
terminator
.
call
(
result
,
object
)
end
end
end
# TODO: Decompose into more Array like behavior
def
replace_or_append!
(
chain
)
if
index
=
index
(
chain
)
self
[
index
]
=
chain
else
self
<<
chain
end
self
end
def
find
(
callback
,
&
block
)
select
{
|
c
|
c
==
callback
&&
(
!
block_given?
||
yield
(
c
))
}.
first
end
def
delete
(
callback
)
super
(
callback
.
is_a?
(
Callback
)
?
callback
:
find
(
callback
))
end
private
def
self
.
extract_options
(
*
methods
,
&
block
)
methods
.
flatten!
options
=
methods
.
extract_options!
methods
<<
block
if
block_given?
return
methods
,
options
end
def
extract_options
(
*
methods
,
&
block
)
self
.
class
.
extract_options
(
*
methods
,
&
block
)
end
end
class
Callback
attr_reader
:kind
,
:method
,
:identifier
,
:options
def
initialize
(
kind
,
method
,
options
=
{})
@kind
=
kind
@method
=
method
@identifier
=
options
[
:identifier
]
@options
=
options
end
def
==
(
other
)
case
other
when
Callback
(
self
.
identifier
&&
self
.
identifier
==
other
.
identifier
)
||
self
.
method
==
other
.
method
else
(
self
.
identifier
&&
self
.
identifier
==
other
)
||
self
.
method
==
other
end
end
def
eql?
(
other
)
self
==
other
end
def
dup
self
.
class
.
new
(
@kind
,
@method
,
@options
.
dup
)
end
def
hash
if
@identifier
@identifier
.
hash
else
@method
.
hash
end
end
def
call
(
*
args
,
&
block
)
evaluate_method
(
method
,
*
args
,
&
block
)
if
should_run_callback?
(
*
args
)
rescue
LocalJumpError
raise
ArgumentError
,
"Cannot yield from a Proc type filter. The Proc must take two "
+
"arguments and execute #call on the second argument."
end
private
def
evaluate_method
(
method
,
*
args
,
&
block
)
case
method
when
Symbol
object
=
args
.
shift
object
.
send
(
method
,
*
args
,
&
block
)
when
String
eval
(
method
,
args
.
first
.
instance_eval
{
binding
})
when
Proc
,
Method
method
.
call
(
*
args
,
&
block
)
else
if
method
.
respond_to?
(
kind
)
method
.
send
(
kind
,
*
args
,
&
block
)
else
raise
ArgumentError
,
"Callbacks must be a symbol denoting the method to call, a string to be evaluated, "
+
"a block to be invoked, or an object responding to the callback method."
end
end
end
def
should_run_callback?
(
*
args
)
[
options
[
:if
]].
flatten
.
compact
.
all?
{
|
a
|
evaluate_method
(
a
,
*
args
)
}
&&
!
[
options
[
:unless
]].
flatten
.
compact
.
any?
{
|
a
|
evaluate_method
(
a
,
*
args
)
}
end
end
def
self
.
included
(
base
)
base
.
extend
ClassMethods
end
module
ClassMethods
def
define_callbacks
(
*
callbacks
)
callbacks
.
each
do
|
callback
|
class_eval
<<-
"end_eval"
,
__FILE__
,
__LINE__
+
1
def self.
#{
callback
}
(*methods, &block) # def self.before_save(*methods, &block)
callbacks = CallbackChain.build(:
#{
callback
}
, *methods, &block) # callbacks = CallbackChain.build(:before_save, *methods, &block)
@
#{
callback
}
_callbacks ||= CallbackChain.new # @before_save_callbacks ||= CallbackChain.new
@
#{
callback
}
_callbacks.concat callbacks # @before_save_callbacks.concat callbacks
end # end
#
def self.
#{
callback
}
_callback_chain # def self.before_save_callback_chain
@
#{
callback
}
_callbacks ||= CallbackChain.new # @before_save_callbacks ||= CallbackChain.new
#
if superclass.respond_to?(:
#{
callback
}
_callback_chain) # if superclass.respond_to?(:before_save_callback_chain)
CallbackChain.new( # CallbackChain.new(
superclass.
#{
callback
}
_callback_chain + # superclass.before_save_callback_chain +
@
#{
callback
}
_callbacks # @before_save_callbacks
) # )
else # else
@
#{
callback
}
_callbacks # @before_save_callbacks
end # end
end # end
end_eval
end
end
end
# Runs all the callbacks defined for the given options.
#
# If a block is given it will be called after each callback receiving as arguments:
#
# * the result from the callback
# * the object which has the callback
#
# If the result from the block evaluates to +true+, the callback chain is stopped.
#
# Example:
# class Storage
# include ActiveSupport::DeprecatedCallbacks
#
# define_callbacks :before_save, :after_save
# end
#
# class ConfigStorage < Storage
# before_save :pass
# before_save :pass
# before_save :stop
# before_save :pass
#
# def pass
# puts "pass"
# end
#
# def stop
# puts "stop"
# return false
# end
#
# def save
# result = run_callbacks(:before_save) { |result, object| result == false }
# puts "- save" if result
# end
# end
#
# config = ConfigStorage.new
# config.save
#
# Output:
# pass
# pass
# stop
def
run_callbacks
(
kind
,
options
=
{},
&
block
)
self
.
class
.
send
(
"
#{
kind
}
_callback_chain"
).
run
(
self
,
options
,
&
block
)
end
end
end
activesupport/lib/active_support/new_callbacks.rb
已删除
100644 → 0
浏览文件 @
9bc8defe
require
'active_support/core_ext/array/wrap'
require
'active_support/core_ext/class/inheritable_attributes'
require
'active_support/core_ext/kernel/reporting'
module
ActiveSupport
# Callbacks are hooks into the lifecycle of an object that allow you to trigger logic
# before or after an alteration of the object state.
#
# Mixing in this module allows you to define callbacks in your class.
#
# Example:
# class Storage
# include ActiveSupport::Callbacks
#
# define_callbacks :save
# end
#
# class ConfigStorage < Storage
# set_callback :save, :before, :saving_message
# def saving_message
# puts "saving..."
# end
#
# set_callback :save, :after do |object|
# puts "saved"
# end
#
# def save
# run_callbacks :save do
# puts "- save"
# end
# end
# end
#
# config = ConfigStorage.new
# config.save
#
# Output:
# saving...
# - save
# saved
#
# Callbacks from parent classes are inherited.
#
# Example:
# class Storage
# include ActiveSupport::Callbacks
#
# define_callbacks :save
#
# set_callback :save, :before, :prepare
# def prepare
# puts "preparing save"
# end
# end
#
# class ConfigStorage < Storage
# set_callback :save, :before, :saving_message
# def saving_message
# puts "saving..."
# end
#
# set_callback :save, :after do |object|
# puts "saved"
# end
#
# def save
# run_callbacks :save do
# puts "- save"
# end
# end
# end
#
# config = ConfigStorage.new
# config.save
#
# Output:
# preparing save
# saving...
# - save
# saved
#
module
NewCallbacks
def
self
.
included
(
klass
)
klass
.
extend
ClassMethods
end
def
run_callbacks
(
kind
,
*
args
,
&
block
)
send
(
"_run_
#{
kind
}
_callbacks"
,
*
args
,
&
block
)
end
class
Callback
@@_callback_sequence
=
0
attr_accessor
:chain
,
:filter
,
:kind
,
:options
,
:per_key
,
:klass
def
initialize
(
chain
,
filter
,
kind
,
options
,
klass
)
@chain
,
@kind
,
@klass
=
chain
,
kind
,
klass
normalize_options!
(
options
)
@per_key
=
options
.
delete
(
:per_key
)
@raw_filter
,
@options
=
filter
,
options
@filter
=
_compile_filter
(
filter
)
@compiled_options
=
_compile_options
(
options
)
@callback_id
=
next_id
_compile_per_key_options
end
def
clone
(
chain
,
klass
)
obj
=
super
()
obj
.
chain
=
chain
obj
.
klass
=
klass
obj
.
per_key
=
@per_key
.
dup
obj
.
options
=
@options
.
dup
obj
.
per_key
[
:if
]
=
@per_key
[
:if
].
dup
obj
.
per_key
[
:unless
]
=
@per_key
[
:unless
].
dup
obj
.
options
[
:if
]
=
@options
[
:if
].
dup
obj
.
options
[
:unless
]
=
@options
[
:unless
].
dup
obj
end
def
normalize_options!
(
options
)
options
[
:if
]
=
Array
.
wrap
(
options
[
:if
])
options
[
:unless
]
=
Array
.
wrap
(
options
[
:unless
])
options
[
:per_key
]
||=
{}
options
[
:per_key
][
:if
]
=
Array
.
wrap
(
options
[
:per_key
][
:if
])
options
[
:per_key
][
:unless
]
=
Array
.
wrap
(
options
[
:per_key
][
:unless
])
end
def
name
chain
.
name
end
def
next_id
@@_callback_sequence
+=
1
end
def
matches?
(
_kind
,
_filter
)
@kind
==
_kind
&&
@filter
==
_filter
end
def
_update_filter
(
filter_options
,
new_options
)
filter_options
[
:if
].
push
(
new_options
[
:unless
])
if
new_options
.
key?
(
:unless
)
filter_options
[
:unless
].
push
(
new_options
[
:if
])
if
new_options
.
key?
(
:if
)
end
def
recompile!
(
_options
,
_per_key
)
_update_filter
(
self
.
options
,
_options
)
_update_filter
(
self
.
per_key
,
_per_key
)
@callback_id
=
next_id
@filter
=
_compile_filter
(
@raw_filter
)
@compiled_options
=
_compile_options
(
@options
)
_compile_per_key_options
end
def
_compile_per_key_options
key_options
=
_compile_options
(
@per_key
)
@klass
.
class_eval
<<-
RUBY_EVAL
,
__FILE__
,
__LINE__
+
1
def _one_time_conditions_valid_
#{
@callback_id
}
?
true
#{
key_options
[
0
]
}
end
RUBY_EVAL
end
# This will supply contents for before and around filters, and no
# contents for after filters (for the forward pass).
def
start
(
key
=
nil
,
object
=
nil
)
return
if
key
&&
!
object
.
send
(
"_one_time_conditions_valid_
#{
@callback_id
}
?"
)
# options[0] is the compiled form of supplied conditions
# options[1] is the "end" for the conditional
#
if
@kind
==
:before
||
@kind
==
:around
if
@kind
==
:before
# if condition # before_save :filter_name, :if => :condition
# filter_name
# end
filter
=
<<-
RUBY_EVAL
unless halted
result =
#{
@filter
}
halted = (
#{
chain
.
config
[
:terminator
]
}
)
end
RUBY_EVAL
[
@compiled_options
[
0
],
filter
,
@compiled_options
[
1
]].
compact
.
join
(
"
\n
"
)
else
# Compile around filters with conditions into proxy methods
# that contain the conditions.
#
# For `around_save :filter_name, :if => :condition':
#
# def _conditional_callback_save_17
# if condition
# filter_name do
# yield self
# end
# else
# yield self
# end
# end
#
name
=
"_conditional_callback_
#{
@kind
}
_
#{
next_id
}
"
txt
,
line
=
<<-
RUBY_EVAL
,
__LINE__
+
1
def
#{
name
}
(halted)
#{
@compiled_options
[
0
]
||
"if true"
}
&& !halted
#{
@filter
}
do
yield self
end
else
yield self
end
end
RUBY_EVAL
@klass
.
class_eval
(
txt
,
__FILE__
,
line
)
"
#{
name
}
(halted) do"
end
end
end
# This will supply contents for around and after filters, but not
# before filters (for the backward pass).
def
end
(
key
=
nil
,
object
=
nil
)
return
if
key
&&
!
object
.
send
(
"_one_time_conditions_valid_
#{
@callback_id
}
?"
)
if
@kind
==
:around
||
@kind
==
:after
# if condition # after_save :filter_name, :if => :condition
# filter_name
# end
if
@kind
==
:after
[
@compiled_options
[
0
],
@filter
,
@compiled_options
[
1
]].
compact
.
join
(
"
\n
"
)
else
"end"
end
end
end
private
# Options support the same options as filters themselves (and support
# symbols, string, procs, and objects), so compile a conditional
# expression based on the options
def
_compile_options
(
options
)
return
[]
if
options
[
:if
].
empty?
&&
options
[
:unless
].
empty?
conditions
=
[]
unless
options
[
:if
].
empty?
conditions
<<
Array
.
wrap
(
_compile_filter
(
options
[
:if
]))
end
unless
options
[
:unless
].
empty?
conditions
<<
Array
.
wrap
(
_compile_filter
(
options
[
:unless
])).
map
{
|
f
|
"!
#{
f
}
"
}
end
[
"if
#{
conditions
.
flatten
.
join
(
" && "
)
}
"
,
"end"
]
end
# Filters support:
#
# Arrays:: Used in conditions. This is used to specify
# multiple conditions. Used internally to
# merge conditions from skip_* filters
# Symbols:: A method to call
# Strings:: Some content to evaluate
# Procs:: A proc to call with the object
# Objects:: An object with a before_foo method on it to call
#
# All of these objects are compiled into methods and handled
# the same after this point:
#
# Arrays:: Merged together into a single filter
# Symbols:: Already methods
# Strings:: class_eval'ed into methods
# Procs:: define_method'ed into methods
# Objects::
# a method is created that calls the before_foo method
# on the object.
#
def
_compile_filter
(
filter
)
method_name
=
"_callback_
#{
@kind
}
_
#{
next_id
}
"
case
filter
when
Array
filter
.
map
{
|
f
|
_compile_filter
(
f
)}
when
Symbol
filter
when
String
"(
#{
filter
}
)"
when
Proc
@klass
.
send
(
:define_method
,
method_name
,
&
filter
)
return
method_name
if
filter
.
arity
<=
0
method_name
<<
(
filter
.
arity
==
1
?
"(self)"
:
" self, Proc.new "
)
else
@klass
.
send
(
:define_method
,
"
#{
method_name
}
_object"
)
{
filter
}
_normalize_legacy_filter
(
kind
,
filter
)
scopes
=
Array
.
wrap
(
chain
.
config
[
:scope
])
method_to_call
=
scopes
.
map
{
|
s
|
s
.
is_a?
(
Symbol
)
?
send
(
s
)
:
s
}.
join
(
"_"
)
@klass
.
class_eval
<<-
RUBY_EVAL
,
__FILE__
,
__LINE__
+
1
def
#{
method_name
}
(&blk)
#{
method_name
}
_object.send(:
#{
method_to_call
}
, self, &blk)
end
RUBY_EVAL
method_name
end
end
def
_normalize_legacy_filter
(
kind
,
filter
)
if
!
filter
.
respond_to?
(
kind
)
&&
filter
.
respond_to?
(
:filter
)
filter
.
metaclass
.
class_eval
(
"def
#{
kind
}
(context, &block) filter(context, &block) end"
,
__FILE__
,
__LINE__
-
1
)
elsif
filter
.
respond_to?
(
:before
)
&&
filter
.
respond_to?
(
:after
)
&&
kind
==
:around
def
filter
.
around
(
context
)
should_continue
=
before
(
context
)
yield
if
should_continue
after
(
context
)
end
end
end
end
# An Array with a compile method
class
CallbackChain
<
Array
attr_reader
:name
,
:config
def
initialize
(
name
,
config
)
@name
=
name
@config
=
{
:terminator
=>
"false"
,
:rescuable
=>
false
,
:scope
=>
[
:kind
]
}.
merge
(
config
)
end
def
compile
(
key
=
nil
,
object
=
nil
)
method
=
[]
method
<<
"value = nil"
method
<<
"halted = false"
each
do
|
callback
|
method
<<
callback
.
start
(
key
,
object
)
end
if
config
[
:rescuable
]
method
<<
"rescued_error = nil"
method
<<
"begin"
end
method
<<
"value = yield if block_given? && !halted"
if
config
[
:rescuable
]
method
<<
"rescue Exception => e"
method
<<
"rescued_error = e"
method
<<
"end"
end
reverse_each
do
|
callback
|
method
<<
callback
.
end
(
key
,
object
)
end
method
<<
"raise rescued_error if rescued_error"
if
config
[
:rescuable
]
method
<<
"halted ? false : (block_given? ? value : true)"
method
.
compact
.
join
(
"
\n
"
)
end
def
clone
(
klass
)
chain
=
CallbackChain
.
new
(
@name
,
@config
.
dup
)
callbacks
=
map
{
|
c
|
c
.
clone
(
chain
,
klass
)
}
chain
.
push
(
*
callbacks
)
end
end
module
ClassMethods
# Make the run_callbacks :save method. The generated method takes
# a block that it'll yield to. It'll call the before and around filters
# in order, yield the block, and then run the after filters.
#
# run_callbacks :save do
# save
# end
#
# The run_callbacks :save method can optionally take a key, which
# will be used to compile an optimized callback method for each
# key. See #define_callbacks for more information.
#
def
__define_runner
(
symbol
)
#:nodoc:
body
=
send
(
"_
#{
symbol
}
_callbacks"
).
compile
(
nil
)
body
,
line
=
<<-
RUBY_EVAL
,
__LINE__
def _run_
#{
symbol
}
_callbacks(key = nil, &blk)
if key
name = "_run__
\#
{self.class.name.hash.abs}__
#{
symbol
}
__
\#
{key.hash.abs}__callbacks"
unless respond_to?(name)
self.class.__create_keyed_callback(name, :
#{
symbol
}
, self, &blk)
end
send(name, &blk)
else
#{
body
}
end
end
private :_run_
#{
symbol
}
_callbacks
RUBY_EVAL
silence_warnings
do
undef_method
"_run_
#{
symbol
}
_callbacks"
if
method_defined?
(
"_run_
#{
symbol
}
_callbacks"
)
class_eval
body
,
__FILE__
,
line
end
end
# This is called the first time a callback is called with a particular
# key. It creates a new callback method for the key, calculating
# which callbacks can be omitted because of per_key conditions.
#
def
__create_keyed_callback
(
name
,
kind
,
object
,
&
blk
)
#:nodoc:
@_keyed_callbacks
||=
{}
@_keyed_callbacks
[
name
]
||=
begin
str
=
send
(
"_
#{
kind
}
_callbacks"
).
compile
(
name
,
object
)
class_eval
"def
#{
name
}
()
#{
str
}
end"
,
__FILE__
,
__LINE__
true
end
end
# This is used internally to append, prepend and skip callbacks to the
# CallbackChain.
#
def
__update_callbacks
(
name
,
filters
=
[],
block
=
nil
)
#:nodoc:
type
=
[
:before
,
:after
,
:around
].
include?
(
filters
.
first
)
?
filters
.
shift
:
:before
options
=
filters
.
last
.
is_a?
(
Hash
)
?
filters
.
pop
:
{}
filters
.
unshift
(
block
)
if
block
chain
=
send
(
"_
#{
name
}
_callbacks"
)
yield
chain
,
type
,
filters
,
options
if
block_given?
__define_runner
(
name
)
end
# Set callbacks for a previously defined callback.
#
# Syntax:
# set_callback :save, :before, :before_meth
# set_callback :save, :after, :after_meth, :if => :condition
# set_callback :save, :around, lambda { |r| stuff; yield; stuff }
#
# Use skip_callback to skip any defined one.
#
# When creating or skipping callbacks, you can specify conditions that
# are always the same for a given key. For instance, in ActionPack,
# we convert :only and :except conditions into per-key conditions.
#
# before_filter :authenticate, :except => "index"
# becomes
# dispatch_callback :before, :authenticate, :per_key => {:unless => proc {|c| c.action_name == "index"}}
#
# Per-Key conditions are evaluated only once per use of a given key.
# In the case of the above example, you would do:
#
# run_callbacks(:dispatch, action_name) { ... dispatch stuff ... }
#
# In that case, each action_name would get its own compiled callback
# method that took into consideration the per_key conditions. This
# is a speed improvement for ActionPack.
#
def
set_callback
(
name
,
*
filters
,
&
block
)
__update_callbacks
(
name
,
filters
,
block
)
do
|
chain
,
type
,
filters
,
options
|
filters
.
map!
do
|
filter
|
chain
.
delete_if
{
|
c
|
c
.
matches?
(
type
,
filter
)
}
Callback
.
new
(
chain
,
filter
,
type
,
options
.
dup
,
self
)
end
options
[
:prepend
]
?
chain
.
unshift
(
*
filters
)
:
chain
.
push
(
*
filters
)
end
end
# Skip a previously defined callback for a given type.
#
def
skip_callback
(
name
,
*
filters
,
&
block
)
__update_callbacks
(
name
,
filters
,
block
)
do
|
chain
,
type
,
filters
,
options
|
chain
=
send
(
"_
#{
name
}
_callbacks="
,
chain
.
clone
(
self
))
filters
.
each
do
|
filter
|
filter
=
chain
.
find
{
|
c
|
c
.
matches?
(
type
,
filter
)
}
if
filter
&&
options
.
any?
filter
.
recompile!
(
options
,
options
[
:per_key
]
||
{})
else
chain
.
delete
(
filter
)
end
end
end
end
# Reset callbacks for a given type.
#
def
reset_callbacks
(
symbol
)
send
(
"_
#{
symbol
}
_callbacks"
).
clear
__define_runner
(
symbol
)
end
# Define callbacks types.
#
# ==== Example
#
# define_callbacks :validate
#
# ==== Options
#
# * <tt>:terminator</tt> - Indicates when a before filter is considered
# to be halted.
#
# define_callbacks :validate, :terminator => "result == false"
#
# In the example above, if any before validate callbacks returns false,
# other callbacks are not executed. Defaults to "false".
#
# * <tt>:rescuable</tt> - By default, after filters are not executed if
# the given block or an before_filter raises an error. Supply :rescuable => true
# to change this behavior.
#
# * <tt>:scope</tt> - Show which methods should be executed when a class
# is giben as callback:
#
# define_callbacks :filters, :scope => [ :kind ]
#
# When a class is given:
#
# before_filter MyFilter
#
# It will call the type of the filter in the given class, which in this
# case, is "before".
#
# If, for instance, you supply the given scope:
#
# define_callbacks :validate, :scope => [ :kind, :name ]
#
# It will call "#{kind}_#{name}" in the given class. So "before_validate"
# will be called in the class below:
#
# before_validate MyValidation
#
# Defaults to :kind.
#
def
define_callbacks
(
*
symbols
)
config
=
symbols
.
last
.
is_a?
(
Hash
)
?
symbols
.
pop
:
{}
symbols
.
each
do
|
symbol
|
extlib_inheritable_accessor
(
"_
#{
symbol
}
_callbacks"
)
do
CallbackChain
.
new
(
symbol
,
config
)
end
__define_runner
(
symbol
)
end
end
end
end
end
activesupport/lib/active_support/testing/setup_and_teardown.rb
浏览文件 @
21e7b846
require
'active_support/callbacks'
module
ActiveSupport
module
Testing
module
SetupAndTeardown
def
self
.
included
(
base
)
base
.
class_eval
do
include
ActiveSupport
::
Callbacks
include
ActiveSupport
::
Deprecated
Callbacks
define_callbacks
:setup
,
:teardown
if
defined?
(
MiniTest
::
Assertions
)
&&
TestCase
<
MiniTest
::
Assertions
...
...
@@ -34,7 +32,7 @@ def run(runner)
result
end
end
module
ForClassicTestUnit
# For compatibility with Ruby < 1.8.6
PASSTHROUGH_EXCEPTIONS
=
Test
::
Unit
::
TestCase
::
PASSTHROUGH_EXCEPTIONS
rescue
[
NoMemoryError
,
SignalException
,
Interrupt
,
SystemExit
]
...
...
activesupport/test/
new_
callback_inheritance_test.rb
→
activesupport/test/callback_inheritance_test.rb
浏览文件 @
21e7b846
...
...
@@ -3,35 +3,35 @@
require
'active_support'
class
GrandParent
include
ActiveSupport
::
New
Callbacks
include
ActiveSupport
::
Callbacks
attr_reader
:log
,
:action_name
def
initialize
(
action_name
)
@action_name
,
@log
=
action_name
,
[]
end
define_callbacks
:dispatch
set_callback
:dispatch
,
:before
,
:before1
,
:before2
,
:per_key
=>
{
:if
=>
proc
{
|
c
|
c
.
action_name
==
"index"
||
c
.
action_name
==
"update"
}}
set_callback
:dispatch
,
:after
,
:after1
,
:after2
,
:per_key
=>
{
:if
=>
proc
{
|
c
|
c
.
action_name
==
"update"
||
c
.
action_name
==
"delete"
}}
set_callback
:dispatch
,
:after
,
:after1
,
:after2
,
:per_key
=>
{
:if
=>
proc
{
|
c
|
c
.
action_name
==
"update"
||
c
.
action_name
==
"delete"
}}
def
before1
@log
<<
"before1"
end
def
before2
@log
<<
"before2"
end
def
after1
def
after1
@log
<<
"after1"
end
def
after2
@log
<<
"after2"
end
def
dispatch
_run_dispatch_callbacks
(
action_name
)
do
run_callbacks
(
:dispatch
,
action_name
)
do
@log
<<
action_name
end
self
...
...
@@ -45,11 +45,11 @@ class Parent < GrandParent
class
Child
<
GrandParent
skip_callback
:dispatch
,
:before
,
:before2
,
:per_key
=>
{
:unless
=>
proc
{
|
c
|
c
.
action_name
==
"update"
}},
:if
=>
:state_open?
def
state_open?
@state
==
:open
end
def
initialize
(
action_name
,
state
)
super
(
action_name
)
@state
=
state
...
...
@@ -64,15 +64,15 @@ def setup
@delete
=
GrandParent
.
new
(
"delete"
).
dispatch
@unknown
=
GrandParent
.
new
(
"unknown"
).
dispatch
end
def
test_basic_per_key1
assert_equal
%w(before1 before2 index)
,
@index
.
log
end
def
test_basic_per_key2
assert_equal
%w(before1 before2 update after2 after1)
,
@update
.
log
end
def
test_basic_per_key3
assert_equal
%w(delete after2 after1)
,
@delete
.
log
end
...
...
@@ -85,15 +85,15 @@ def setup
@delete
=
Parent
.
new
(
"delete"
).
dispatch
@unknown
=
Parent
.
new
(
"unknown"
).
dispatch
end
def
test_inherited_excluded
assert_equal
%w(before1 index)
,
@index
.
log
end
def
test_inherited_not_excluded
assert_equal
%w(before1 before2 update after1)
,
@update
.
log
end
def
test_partially_excluded
assert_equal
%w(delete after2 after1)
,
@delete
.
log
end
...
...
@@ -104,11 +104,11 @@ def setup
@update1
=
Child
.
new
(
"update"
,
:open
).
dispatch
@update2
=
Child
.
new
(
"update"
,
:closed
).
dispatch
end
def
test_crazy_mix_on
assert_equal
%w(before1 update after2 after1)
,
@update1
.
log
end
def
test_crazy_mix_off
assert_equal
%w(before1 before2 update after2 after1)
,
@update2
.
log
end
...
...
activesupport/test/callbacks_test.rb
浏览文件 @
21e7b846
require
'abstract_unit'
# require 'abstract_unit'
require
'test/unit'
$:
.
unshift
"
#{
File
.
dirname
(
__FILE__
)
}
/../lib"
require
'active_support'
class
Record
include
ActiveSupport
::
Callbacks
module
CallbacksTest
class
Record
include
ActiveSupport
::
Callbacks
define_callbacks
:before_save
,
:after_
save
define_callbacks
:
save
class
<<
self
def
callback_symbol
(
callback_method
)
method_name
=
"
#{
callback_method
}
_method"
define_method
(
method_name
)
do
history
<<
[
callback_method
,
:symbol
]
def
self
.
before_save
(
*
filters
,
&
blk
)
set_callback
(
:save
,
:before
,
*
filters
,
&
blk
)
end
def
self
.
after_save
(
*
filters
,
&
blk
)
set_callback
(
:save
,
:after
,
*
filters
,
&
blk
)
end
class
<<
self
def
callback_symbol
(
callback_method
)
method_name
=
:"
#{
callback_method
}
_method"
define_method
(
method_name
)
do
history
<<
[
callback_method
,
:symbol
]
end
method_name
end
def
callback_string
(
callback_method
)
"history << [
#{
callback_method
.
to_sym
.
inspect
}
, :string]"
end
def
callback_proc
(
callback_method
)
Proc
.
new
{
|
model
|
model
.
history
<<
[
callback_method
,
:proc
]
}
end
def
callback_object
(
callback_method
)
klass
=
Class
.
new
klass
.
send
(
:define_method
,
callback_method
)
do
|
model
|
model
.
history
<<
[
:"
#{
callback_method
}
_save"
,
:object
]
end
klass
.
new
end
method_name
end
def
callback_string
(
callback_method
)
"history << [
#{
callback_method
.
to_sym
.
inspect
}
, :string]"
def
history
@history
||=
[]
end
end
class
Person
<
Record
[
:before_save
,
:after_save
].
each
do
|
callback_method
|
callback_method_sym
=
callback_method
.
to_sym
send
(
callback_method
,
callback_symbol
(
callback_method_sym
))
send
(
callback_method
,
callback_string
(
callback_method_sym
))
send
(
callback_method
,
callback_proc
(
callback_method_sym
))
send
(
callback_method
,
callback_object
(
callback_method_sym
.
to_s
.
gsub
(
/_save/
,
''
)))
send
(
callback_method
)
{
|
model
|
model
.
history
<<
[
callback_method_sym
,
:block
]
}
end
def
callback_proc
(
callback_method
)
Proc
.
new
{
|
model
|
model
.
history
<<
[
callback_method
,
:proc
]
}
def
save
run_callbacks
:save
end
end
class
PersonSkipper
<
Person
skip_callback
:save
,
:before
,
:before_save_method
,
:if
=>
:yes
skip_callback
:save
,
:after
,
:before_save_method
,
:unless
=>
:yes
skip_callback
:save
,
:after
,
:before_save_method
,
:if
=>
:no
skip_callback
:save
,
:before
,
:before_save_method
,
:unless
=>
:no
def
yes
;
true
;
end
def
no
;
false
;
end
end
class
ParentController
include
ActiveSupport
::
Callbacks
define_callbacks
:dispatch
set_callback
:dispatch
,
:before
,
:log
,
:per_key
=>
{
:unless
=>
proc
{
|
c
|
c
.
action_name
==
:index
||
c
.
action_name
==
:show
}}
set_callback
:dispatch
,
:after
,
:log2
def
callback_object
(
callback_method
)
klass
=
Class
.
new
klass
.
send
(
:define_method
,
callback_method
)
do
|
model
|
model
.
history
<<
[
callback_method
,
:object
]
attr_reader
:action_name
,
:logger
def
initialize
(
action_name
)
@action_name
,
@logger
=
action_name
,
[]
end
def
log
@logger
<<
action_name
end
def
log2
@logger
<<
action_name
end
def
dispatch
run_callbacks
:dispatch
,
action_name
do
@logger
<<
"Done"
end
klass
.
new
self
end
end
def
history
@history
||=
[]
class
Child
<
ParentController
skip_callback
:dispatch
,
:before
,
:log
,
:per_key
=>
{
:if
=>
proc
{
|
c
|
c
.
action_name
==
:update
}
}
skip_callback
:dispatch
,
:after
,
:log2
end
end
class
Person
<
Record
[
:before_save
,
:after_save
].
each
do
|
callback_method
|
callback_method_sym
=
callback_method
.
to_sym
send
(
callback_method
,
callback_symbol
(
callback_method_sym
))
send
(
callback_method
,
callback_string
(
callback_method_sym
))
send
(
callback_method
,
callback_proc
(
callback_method_sym
))
send
(
callback_method
,
callback_object
(
callback_method_sym
))
send
(
callback_method
)
{
|
model
|
model
.
history
<<
[
callback_method_sym
,
:block
]
}
class
OneTimeCompile
<
Record
@@starts_true
,
@@starts_false
=
true
,
false
def
initialize
super
end
before_save
Proc
.
new
{
|
r
|
r
.
history
<<
[
:before_save
,
:starts_true
,
:if
]
},
:per_key
=>
{
:if
=>
:starts_true
}
before_save
Proc
.
new
{
|
r
|
r
.
history
<<
[
:before_save
,
:starts_false
,
:if
]
},
:per_key
=>
{
:if
=>
:starts_false
}
before_save
Proc
.
new
{
|
r
|
r
.
history
<<
[
:before_save
,
:starts_true
,
:unless
]
},
:per_key
=>
{
:unless
=>
:starts_true
}
before_save
Proc
.
new
{
|
r
|
r
.
history
<<
[
:before_save
,
:starts_false
,
:unless
]
},
:per_key
=>
{
:unless
=>
:starts_false
}
def
starts_true
if
@@starts_true
@@starts_true
=
false
return
true
end
@@starts_true
end
def
starts_false
unless
@@starts_false
@@starts_false
=
true
return
false
end
@@starts_false
end
def
save
run_callbacks
:save
,
:action
end
end
def
save
run_callbacks
(
:before_save
)
run_callbacks
(
:after_save
)
class
OneTimeCompileTest
<
Test
::
Unit
::
TestCase
def
test_optimized_first_compile
around
=
OneTimeCompile
.
new
around
.
save
assert_equal
[
[
:before_save
,
:starts_true
,
:if
],
[
:before_save
,
:starts_true
,
:unless
]
],
around
.
history
end
end
end
class
ConditionalPerson
<
Record
# proc
before_save
Proc
.
new
{
|
r
|
r
.
history
<<
[
:before_save
,
:proc
]
},
:if
=>
Proc
.
new
{
|
r
|
true
}
before_save
Proc
.
new
{
|
r
|
r
.
history
<<
"b00m"
},
:if
=>
Proc
.
new
{
|
r
|
false
}
before_save
Proc
.
new
{
|
r
|
r
.
history
<<
[
:before_save
,
:proc
]
},
:unless
=>
Proc
.
new
{
|
r
|
false
}
before_save
Proc
.
new
{
|
r
|
r
.
history
<<
"b00m"
},
:unless
=>
Proc
.
new
{
|
r
|
true
}
# symbol
before_save
Proc
.
new
{
|
r
|
r
.
history
<<
[
:before_save
,
:symbol
]
},
:if
=>
:yes
before_save
Proc
.
new
{
|
r
|
r
.
history
<<
"b00m"
},
:if
=>
:no
before_save
Proc
.
new
{
|
r
|
r
.
history
<<
[
:before_save
,
:symbol
]
},
:unless
=>
:no
before_save
Proc
.
new
{
|
r
|
r
.
history
<<
"b00m"
},
:unless
=>
:yes
# string
before_save
Proc
.
new
{
|
r
|
r
.
history
<<
[
:before_save
,
:string
]
},
:if
=>
'yes'
before_save
Proc
.
new
{
|
r
|
r
.
history
<<
"b00m"
},
:if
=>
'no'
before_save
Proc
.
new
{
|
r
|
r
.
history
<<
[
:before_save
,
:string
]
},
:unless
=>
'no'
before_save
Proc
.
new
{
|
r
|
r
.
history
<<
"b00m"
},
:unless
=>
'yes'
# Array with conditions
before_save
Proc
.
new
{
|
r
|
r
.
history
<<
[
:before_save
,
:symbol_array
]
},
:if
=>
[
:yes
,
:other_yes
]
before_save
Proc
.
new
{
|
r
|
r
.
history
<<
"b00m"
},
:if
=>
[
:yes
,
:no
]
before_save
Proc
.
new
{
|
r
|
r
.
history
<<
[
:before_save
,
:symbol_array
]
},
:unless
=>
[
:no
,
:other_no
]
before_save
Proc
.
new
{
|
r
|
r
.
history
<<
"b00m"
},
:unless
=>
[
:yes
,
:no
]
# Combined if and unless
before_save
Proc
.
new
{
|
r
|
r
.
history
<<
[
:before_save
,
:combined_symbol
]
},
:if
=>
:yes
,
:unless
=>
:no
before_save
Proc
.
new
{
|
r
|
r
.
history
<<
"b00m"
},
:if
=>
:yes
,
:unless
=>
:yes
# Array with different types of conditions
before_save
Proc
.
new
{
|
r
|
r
.
history
<<
[
:before_save
,
:symbol_proc_string_array
]
},
:if
=>
[
:yes
,
Proc
.
new
{
|
r
|
true
},
'yes'
]
before_save
Proc
.
new
{
|
r
|
r
.
history
<<
"b00m"
},
:if
=>
[
:yes
,
Proc
.
new
{
|
r
|
true
},
'no'
]
# Array with different types of conditions comibned if and unless
before_save
Proc
.
new
{
|
r
|
r
.
history
<<
[
:before_save
,
:combined_symbol_proc_string_array
]
},
:if
=>
[
:yes
,
Proc
.
new
{
|
r
|
true
},
'yes'
],
:unless
=>
[
:no
,
'no'
]
before_save
Proc
.
new
{
|
r
|
r
.
history
<<
"b00m"
},
:if
=>
[
:yes
,
Proc
.
new
{
|
r
|
true
},
'no'
],
:unless
=>
[
:no
,
'no'
]
def
yes
;
true
;
end
def
other_yes
;
true
;
end
def
no
;
false
;
end
def
other_no
;
false
;
end
def
save
run_callbacks
(
:before_save
)
run_callbacks
(
:after_save
)
class
ConditionalPerson
<
Record
# proc
before_save
Proc
.
new
{
|
r
|
r
.
history
<<
[
:before_save
,
:proc
]
},
:if
=>
Proc
.
new
{
|
r
|
true
}
before_save
Proc
.
new
{
|
r
|
r
.
history
<<
"b00m"
},
:if
=>
Proc
.
new
{
|
r
|
false
}
before_save
Proc
.
new
{
|
r
|
r
.
history
<<
[
:before_save
,
:proc
]
},
:unless
=>
Proc
.
new
{
|
r
|
false
}
before_save
Proc
.
new
{
|
r
|
r
.
history
<<
"b00m"
},
:unless
=>
Proc
.
new
{
|
r
|
true
}
# symbol
before_save
Proc
.
new
{
|
r
|
r
.
history
<<
[
:before_save
,
:symbol
]
},
:if
=>
:yes
before_save
Proc
.
new
{
|
r
|
r
.
history
<<
"b00m"
},
:if
=>
:no
before_save
Proc
.
new
{
|
r
|
r
.
history
<<
[
:before_save
,
:symbol
]
},
:unless
=>
:no
before_save
Proc
.
new
{
|
r
|
r
.
history
<<
"b00m"
},
:unless
=>
:yes
# string
before_save
Proc
.
new
{
|
r
|
r
.
history
<<
[
:before_save
,
:string
]
},
:if
=>
'yes'
before_save
Proc
.
new
{
|
r
|
r
.
history
<<
"b00m"
},
:if
=>
'no'
before_save
Proc
.
new
{
|
r
|
r
.
history
<<
[
:before_save
,
:string
]
},
:unless
=>
'no'
before_save
Proc
.
new
{
|
r
|
r
.
history
<<
"b00m"
},
:unless
=>
'yes'
# Combined if and unless
before_save
Proc
.
new
{
|
r
|
r
.
history
<<
[
:before_save
,
:combined_symbol
]
},
:if
=>
:yes
,
:unless
=>
:no
before_save
Proc
.
new
{
|
r
|
r
.
history
<<
"b00m"
},
:if
=>
:yes
,
:unless
=>
:yes
def
yes
;
true
;
end
def
other_yes
;
true
;
end
def
no
;
false
;
end
def
other_no
;
false
;
end
def
save
run_callbacks
:save
end
end
end
class
CallbacksTest
<
Test
::
Unit
::
TestCase
def
test_save_person
person
=
Person
.
new
assert_equal
[],
person
.
history
person
.
save
assert_equal
[
[
:before_save
,
:symbol
],
[
:before_save
,
:string
],
[
:before_save
,
:proc
],
[
:before_save
,
:object
],
[
:before_save
,
:block
],
[
:after_save
,
:symbol
],
[
:after_save
,
:string
],
[
:after_save
,
:proc
],
[
:after_save
,
:object
],
[
:after_save
,
:block
]
],
person
.
history
class
CleanPerson
<
ConditionalPerson
reset_callbacks
:save
end
end
class
ConditionalCallbackTest
<
Test
::
Unit
::
TestCase
def
test_save_conditional_person
person
=
ConditionalPerson
.
new
person
.
save
assert_equal
[
[
:before_save
,
:proc
],
[
:before_save
,
:proc
],
[
:before_save
,
:symbol
],
[
:before_save
,
:symbol
],
[
:before_save
,
:string
],
[
:before_save
,
:string
],
[
:before_save
,
:symbol_array
],
[
:before_save
,
:symbol_array
],
[
:before_save
,
:combined_symbol
],
[
:before_save
,
:symbol_proc_string_array
],
[
:before_save
,
:combined_symbol_proc_string_array
]
],
person
.
history
class
MySuper
include
ActiveSupport
::
Callbacks
define_callbacks
:save
end
end
class
CallbackTest
<
Test
::
Unit
::
TestCase
include
ActiveSupport
::
Callbacks
class
AroundPerson
<
MySuper
attr_reader
:history
set_callback
:save
,
:before
,
:nope
,
:if
=>
:no
set_callback
:save
,
:before
,
:nope
,
:unless
=>
:yes
set_callback
:save
,
:after
,
:tweedle
set_callback
:save
,
:before
,
"tweedle_dee"
set_callback
:save
,
:before
,
proc
{
|
m
|
m
.
history
<<
"yup"
}
set_callback
:save
,
:before
,
:nope
,
:if
=>
proc
{
false
}
set_callback
:save
,
:before
,
:nope
,
:unless
=>
proc
{
true
}
set_callback
:save
,
:before
,
:yup
,
:if
=>
proc
{
true
}
set_callback
:save
,
:before
,
:yup
,
:unless
=>
proc
{
false
}
set_callback
:save
,
:around
,
:tweedle_dum
set_callback
:save
,
:around
,
:w0tyes
,
:if
=>
:yes
set_callback
:save
,
:around
,
:w0tno
,
:if
=>
:no
set_callback
:save
,
:around
,
:tweedle_deedle
def
no
;
false
;
end
def
yes
;
true
;
end
def
nope
@history
<<
"boom"
end
def
yup
@history
<<
"yup"
end
def
w0tyes
@history
<<
"w0tyes before"
yield
@history
<<
"w0tyes after"
end
def
w0tno
@history
<<
"boom"
yield
end
def
tweedle_dee
@history
<<
"tweedle dee"
end
def
tweedle_dum
@history
<<
"tweedle dum pre"
yield
@history
<<
"tweedle dum post"
end
def
tweedle
@history
<<
"tweedle"
end
def
tweedle_deedle
@history
<<
"tweedle deedle pre"
yield
@history
<<
"tweedle deedle post"
end
def
initialize
@history
=
[]
end
def
test_eql
callback
=
Callback
.
new
(
:before
,
:save
,
:identifier
=>
:lifesaver
)
assert
callback
.
eql?
(
Callback
.
new
(
:before
,
:save
,
:identifier
=>
:lifesaver
))
assert
callback
.
eql?
(
Callback
.
new
(
:before
,
:save
))
assert
callback
.
eql?
(
:lifesaver
)
assert
callback
.
eql?
(
:save
)
assert
!
callback
.
eql?
(
Callback
.
new
(
:before
,
:destroy
))
assert
!
callback
.
eql?
(
:destroy
)
def
save
run_callbacks
:save
do
@history
<<
"running"
end
end
end
def
test_dup
a
=
Callback
.
new
(
:before
,
:save
)
assert_equal
({},
a
.
options
)
b
=
a
.
dup
b
.
options
[
:unless
]
=
:pigs_fly
assert_equal
({
:unless
=>
:pigs_fly
},
b
.
options
)
assert_equal
({},
a
.
options
)
class
HyphenatedCallbacks
include
ActiveSupport
::
Callbacks
define_callbacks
:save
attr_reader
:stuff
set_callback
:save
,
:before
,
:omg
,
:per_key
=>
{
:if
=>
:yes
}
def
yes
()
true
end
def
omg
@stuff
=
"OMG"
end
def
save
run_callbacks
:save
,
"hyphen-ated"
do
@stuff
end
end
end
class
AroundCallbacksTest
<
Test
::
Unit
::
TestCase
def
test_save_around
around
=
AroundPerson
.
new
around
.
save
assert_equal
[
"tweedle dee"
,
"yup"
,
"yup"
,
"tweedle dum pre"
,
"w0tyes before"
,
"tweedle deedle pre"
,
"running"
,
"tweedle deedle post"
,
"w0tyes after"
,
"tweedle dum post"
,
"tweedle"
],
around
.
history
end
end
class
SkipCallbacksTest
<
Test
::
Unit
::
TestCase
def
test_skip_person
person
=
PersonSkipper
.
new
assert_equal
[],
person
.
history
person
.
save
assert_equal
[
[
:before_save
,
:string
],
[
:before_save
,
:proc
],
[
:before_save
,
:object
],
[
:before_save
,
:block
],
[
:after_save
,
:block
],
[
:after_save
,
:object
],
[
:after_save
,
:proc
],
[
:after_save
,
:string
],
[
:after_save
,
:symbol
]
],
person
.
history
end
end
class
CallbacksTest
<
Test
::
Unit
::
TestCase
def
test_save_person
person
=
Person
.
new
assert_equal
[],
person
.
history
person
.
save
assert_equal
[
[
:before_save
,
:symbol
],
[
:before_save
,
:string
],
[
:before_save
,
:proc
],
[
:before_save
,
:object
],
[
:before_save
,
:block
],
[
:after_save
,
:block
],
[
:after_save
,
:object
],
[
:after_save
,
:proc
],
[
:after_save
,
:string
],
[
:after_save
,
:symbol
]
],
person
.
history
end
end
class
ConditionalCallbackTest
<
Test
::
Unit
::
TestCase
def
test_save_conditional_person
person
=
ConditionalPerson
.
new
person
.
save
assert_equal
[
[
:before_save
,
:proc
],
[
:before_save
,
:proc
],
[
:before_save
,
:symbol
],
[
:before_save
,
:symbol
],
[
:before_save
,
:string
],
[
:before_save
,
:string
],
[
:before_save
,
:combined_symbol
],
],
person
.
history
end
end
class
ResetCallbackTest
<
Test
::
Unit
::
TestCase
def
test_save_conditional_person
person
=
CleanPerson
.
new
person
.
save
assert_equal
[],
person
.
history
end
end
class
CallbackTerminator
include
ActiveSupport
::
Callbacks
define_callbacks
:save
,
:terminator
=>
"result == :halt"
set_callback
:save
,
:before
,
:first
set_callback
:save
,
:before
,
:second
set_callback
:save
,
:around
,
:around_it
set_callback
:save
,
:before
,
:third
set_callback
:save
,
:after
,
:first
set_callback
:save
,
:around
,
:around_it
set_callback
:save
,
:after
,
:second
set_callback
:save
,
:around
,
:around_it
set_callback
:save
,
:after
,
:third
attr_reader
:history
,
:saved
def
initialize
@history
=
[]
end
def
around_it
@history
<<
"around1"
yield
@history
<<
"around2"
end
def
first
@history
<<
"first"
end
def
second
@history
<<
"second"
:halt
end
def
third
@history
<<
"third"
end
def
save
run_callbacks
:save
do
@saved
=
true
end
end
end
class
CallbackObject
def
before
(
caller
)
caller
.
record
<<
"before"
end
def
before_save
(
caller
)
caller
.
record
<<
"before save"
end
def
around
(
caller
)
caller
.
record
<<
"around before"
yield
caller
.
record
<<
"around after"
end
end
class
UsingObjectBefore
include
ActiveSupport
::
Callbacks
define_callbacks
:save
set_callback
:save
,
:before
,
CallbackObject
.
new
attr_accessor
:record
def
initialize
@record
=
[]
end
def
save
run_callbacks
:save
do
@record
<<
"yielded"
end
end
end
end
class
CallbackChainTest
<
Test
::
Unit
::
TestCase
include
ActiveSupport
::
Callbacks
class
UsingObjectAround
include
ActiveSupport
::
Callbacks
def
setup
@chain
=
CallbackChain
.
build
(
:make
,
:bacon
,
:lettuce
,
:tomato
)
define_callbacks
:save
set_callback
:save
,
:around
,
CallbackObject
.
new
attr_accessor
:record
def
initialize
@record
=
[]
end
def
save
run_callbacks
:save
do
@record
<<
"yielded"
end
end
end
def
test_build
assert_equal
3
,
@chain
.
size
assert_equal
[
:bacon
,
:lettuce
,
:tomato
],
@chain
.
map
(
&
:method
)
class
CustomScopeObject
include
ActiveSupport
::
Callbacks
define_callbacks
:save
,
:scope
=>
[
:kind
,
:name
]
set_callback
:save
,
:before
,
CallbackObject
.
new
attr_accessor
:record
def
initialize
@record
=
[]
end
def
save
run_callbacks
:save
do
@record
<<
"yielded"
"CallbackResult"
end
end
end
def
test_find
assert_equal
:bacon
,
@chain
.
find
(
:bacon
).
method
class
UsingObjectTest
<
Test
::
Unit
::
TestCase
def
test_before_object
u
=
UsingObjectBefore
.
new
u
.
save
assert_equal
[
"before"
,
"yielded"
],
u
.
record
end
def
test_around_object
u
=
UsingObjectAround
.
new
u
.
save
assert_equal
[
"around before"
,
"yielded"
,
"around after"
],
u
.
record
end
def
test_customized_object
u
=
CustomScopeObject
.
new
u
.
save
assert_equal
[
"before save"
,
"yielded"
],
u
.
record
end
def
test_block_result_is_returned
u
=
CustomScopeObject
.
new
assert_equal
"CallbackResult"
,
u
.
save
end
end
def
test_replace_or_append
assert_equal
[
:bacon
,
:lettuce
,
:tomato
],
(
@chain
.
replace_or_append!
(
Callback
.
new
(
:make
,
:bacon
))).
map
(
&
:method
)
assert_equal
[
:bacon
,
:lettuce
,
:tomato
,
:turkey
],
(
@chain
.
replace_or_append!
(
Callback
.
new
(
:make
,
:turkey
))).
map
(
&
:method
)
assert_equal
[
:bacon
,
:lettuce
,
:tomato
,
:turkey
,
:mayo
],
(
@chain
.
replace_or_append!
(
Callback
.
new
(
:make
,
:mayo
))).
map
(
&
:method
)
class
CallbackTerminatorTest
<
Test
::
Unit
::
TestCase
def
test_termination
terminator
=
CallbackTerminator
.
new
terminator
.
save
assert_equal
[
"first"
,
"second"
,
"third"
,
"second"
,
"first"
],
terminator
.
history
end
def
test_block_never_called_if_terminated
obj
=
CallbackTerminator
.
new
obj
.
save
assert
!
obj
.
saved
end
end
def
test_delete
assert_equal
[
:bacon
,
:lettuce
,
:tomato
],
@chain
.
map
(
&
:method
)
@chain
.
delete
(
:bacon
)
assert_equal
[
:lettuce
,
:tomato
],
@chain
.
map
(
&
:method
)
class
HyphenatedKeyTest
<
Test
::
Unit
::
TestCase
def
test_save
obj
=
HyphenatedCallbacks
.
new
obj
.
save
assert_equal
obj
.
stuff
,
"OMG"
end
end
end
activesupport/test/new_callbacks_test.rb
已删除
100644 → 0
浏览文件 @
9bc8defe
# require 'abstract_unit'
require
'test/unit'
$:
.
unshift
"
#{
File
.
dirname
(
__FILE__
)
}
/../lib"
require
'active_support'
module
NewCallbacksTest
class
Record
include
ActiveSupport
::
NewCallbacks
define_callbacks
:save
def
self
.
before_save
(
*
filters
,
&
blk
)
set_callback
(
:save
,
:before
,
*
filters
,
&
blk
)
end
def
self
.
after_save
(
*
filters
,
&
blk
)
set_callback
(
:save
,
:after
,
*
filters
,
&
blk
)
end
class
<<
self
def
callback_symbol
(
callback_method
)
method_name
=
:"
#{
callback_method
}
_method"
define_method
(
method_name
)
do
history
<<
[
callback_method
,
:symbol
]
end
method_name
end
def
callback_string
(
callback_method
)
"history << [
#{
callback_method
.
to_sym
.
inspect
}
, :string]"
end
def
callback_proc
(
callback_method
)
Proc
.
new
{
|
model
|
model
.
history
<<
[
callback_method
,
:proc
]
}
end
def
callback_object
(
callback_method
)
klass
=
Class
.
new
klass
.
send
(
:define_method
,
callback_method
)
do
|
model
|
model
.
history
<<
[
:"
#{
callback_method
}
_save"
,
:object
]
end
klass
.
new
end
end
def
history
@history
||=
[]
end
end
class
Person
<
Record
[
:before_save
,
:after_save
].
each
do
|
callback_method
|
callback_method_sym
=
callback_method
.
to_sym
send
(
callback_method
,
callback_symbol
(
callback_method_sym
))
send
(
callback_method
,
callback_string
(
callback_method_sym
))
send
(
callback_method
,
callback_proc
(
callback_method_sym
))
send
(
callback_method
,
callback_object
(
callback_method_sym
.
to_s
.
gsub
(
/_save/
,
''
)))
send
(
callback_method
)
{
|
model
|
model
.
history
<<
[
callback_method_sym
,
:block
]
}
end
def
save
_run_save_callbacks
{}
end
end
class
PersonSkipper
<
Person
skip_callback
:save
,
:before
,
:before_save_method
,
:if
=>
:yes
skip_callback
:save
,
:after
,
:before_save_method
,
:unless
=>
:yes
skip_callback
:save
,
:after
,
:before_save_method
,
:if
=>
:no
skip_callback
:save
,
:before
,
:before_save_method
,
:unless
=>
:no
def
yes
;
true
;
end
def
no
;
false
;
end
end
class
ParentController
include
ActiveSupport
::
NewCallbacks
define_callbacks
:dispatch
set_callback
:dispatch
,
:before
,
:log
,
:per_key
=>
{
:unless
=>
proc
{
|
c
|
c
.
action_name
==
:index
||
c
.
action_name
==
:show
}}
set_callback
:dispatch
,
:after
,
:log2
attr_reader
:action_name
,
:logger
def
initialize
(
action_name
)
@action_name
,
@logger
=
action_name
,
[]
end
def
log
@logger
<<
action_name
end
def
log2
@logger
<<
action_name
end
def
dispatch
_run_dispatch_callbacks
(
action_name
)
{
@logger
<<
"Done"
}
self
end
end
class
Child
<
ParentController
skip_callback
:dispatch
,
:before
,
:log
,
:per_key
=>
{
:if
=>
proc
{
|
c
|
c
.
action_name
==
:update
}
}
skip_callback
:dispatch
,
:after
,
:log2
end
class
OneTimeCompile
<
Record
@@starts_true
,
@@starts_false
=
true
,
false
def
initialize
super
end
before_save
Proc
.
new
{
|
r
|
r
.
history
<<
[
:before_save
,
:starts_true
,
:if
]
},
:per_key
=>
{
:if
=>
:starts_true
}
before_save
Proc
.
new
{
|
r
|
r
.
history
<<
[
:before_save
,
:starts_false
,
:if
]
},
:per_key
=>
{
:if
=>
:starts_false
}
before_save
Proc
.
new
{
|
r
|
r
.
history
<<
[
:before_save
,
:starts_true
,
:unless
]
},
:per_key
=>
{
:unless
=>
:starts_true
}
before_save
Proc
.
new
{
|
r
|
r
.
history
<<
[
:before_save
,
:starts_false
,
:unless
]
},
:per_key
=>
{
:unless
=>
:starts_false
}
def
starts_true
if
@@starts_true
@@starts_true
=
false
return
true
end
@@starts_true
end
def
starts_false
unless
@@starts_false
@@starts_false
=
true
return
false
end
@@starts_false
end
def
save
_run_save_callbacks
(
:action
)
{}
end
end
class
OneTimeCompileTest
<
Test
::
Unit
::
TestCase
def
test_optimized_first_compile
around
=
OneTimeCompile
.
new
around
.
save
assert_equal
[
[
:before_save
,
:starts_true
,
:if
],
[
:before_save
,
:starts_true
,
:unless
]
],
around
.
history
end
end
class
ConditionalPerson
<
Record
# proc
before_save
Proc
.
new
{
|
r
|
r
.
history
<<
[
:before_save
,
:proc
]
},
:if
=>
Proc
.
new
{
|
r
|
true
}
before_save
Proc
.
new
{
|
r
|
r
.
history
<<
"b00m"
},
:if
=>
Proc
.
new
{
|
r
|
false
}
before_save
Proc
.
new
{
|
r
|
r
.
history
<<
[
:before_save
,
:proc
]
},
:unless
=>
Proc
.
new
{
|
r
|
false
}
before_save
Proc
.
new
{
|
r
|
r
.
history
<<
"b00m"
},
:unless
=>
Proc
.
new
{
|
r
|
true
}
# symbol
before_save
Proc
.
new
{
|
r
|
r
.
history
<<
[
:before_save
,
:symbol
]
},
:if
=>
:yes
before_save
Proc
.
new
{
|
r
|
r
.
history
<<
"b00m"
},
:if
=>
:no
before_save
Proc
.
new
{
|
r
|
r
.
history
<<
[
:before_save
,
:symbol
]
},
:unless
=>
:no
before_save
Proc
.
new
{
|
r
|
r
.
history
<<
"b00m"
},
:unless
=>
:yes
# string
before_save
Proc
.
new
{
|
r
|
r
.
history
<<
[
:before_save
,
:string
]
},
:if
=>
'yes'
before_save
Proc
.
new
{
|
r
|
r
.
history
<<
"b00m"
},
:if
=>
'no'
before_save
Proc
.
new
{
|
r
|
r
.
history
<<
[
:before_save
,
:string
]
},
:unless
=>
'no'
before_save
Proc
.
new
{
|
r
|
r
.
history
<<
"b00m"
},
:unless
=>
'yes'
# Combined if and unless
before_save
Proc
.
new
{
|
r
|
r
.
history
<<
[
:before_save
,
:combined_symbol
]
},
:if
=>
:yes
,
:unless
=>
:no
before_save
Proc
.
new
{
|
r
|
r
.
history
<<
"b00m"
},
:if
=>
:yes
,
:unless
=>
:yes
def
yes
;
true
;
end
def
other_yes
;
true
;
end
def
no
;
false
;
end
def
other_no
;
false
;
end
def
save
_run_save_callbacks
{}
end
end
class
CleanPerson
<
ConditionalPerson
reset_callbacks
:save
end
class
MySuper
include
ActiveSupport
::
NewCallbacks
define_callbacks
:save
end
class
AroundPerson
<
MySuper
attr_reader
:history
set_callback
:save
,
:before
,
:nope
,
:if
=>
:no
set_callback
:save
,
:before
,
:nope
,
:unless
=>
:yes
set_callback
:save
,
:after
,
:tweedle
set_callback
:save
,
:before
,
"tweedle_dee"
set_callback
:save
,
:before
,
proc
{
|
m
|
m
.
history
<<
"yup"
}
set_callback
:save
,
:before
,
:nope
,
:if
=>
proc
{
false
}
set_callback
:save
,
:before
,
:nope
,
:unless
=>
proc
{
true
}
set_callback
:save
,
:before
,
:yup
,
:if
=>
proc
{
true
}
set_callback
:save
,
:before
,
:yup
,
:unless
=>
proc
{
false
}
set_callback
:save
,
:around
,
:tweedle_dum
set_callback
:save
,
:around
,
:w0tyes
,
:if
=>
:yes
set_callback
:save
,
:around
,
:w0tno
,
:if
=>
:no
set_callback
:save
,
:around
,
:tweedle_deedle
def
no
;
false
;
end
def
yes
;
true
;
end
def
nope
@history
<<
"boom"
end
def
yup
@history
<<
"yup"
end
def
w0tyes
@history
<<
"w0tyes before"
yield
@history
<<
"w0tyes after"
end
def
w0tno
@history
<<
"boom"
yield
end
def
tweedle_dee
@history
<<
"tweedle dee"
end
def
tweedle_dum
@history
<<
"tweedle dum pre"
yield
@history
<<
"tweedle dum post"
end
def
tweedle
@history
<<
"tweedle"
end
def
tweedle_deedle
@history
<<
"tweedle deedle pre"
yield
@history
<<
"tweedle deedle post"
end
def
initialize
@history
=
[]
end
def
save
_run_save_callbacks
do
@history
<<
"running"
end
end
end
class
HyphenatedCallbacks
include
ActiveSupport
::
NewCallbacks
define_callbacks
:save
attr_reader
:stuff
set_callback
:save
,
:before
,
:omg
,
:per_key
=>
{
:if
=>
:yes
}
def
yes
()
true
end
def
omg
@stuff
=
"OMG"
end
def
save
_run_save_callbacks
(
"hyphen-ated"
)
do
@stuff
end
end
end
class
AroundCallbacksTest
<
Test
::
Unit
::
TestCase
def
test_save_around
around
=
AroundPerson
.
new
around
.
save
assert_equal
[
"tweedle dee"
,
"yup"
,
"yup"
,
"tweedle dum pre"
,
"w0tyes before"
,
"tweedle deedle pre"
,
"running"
,
"tweedle deedle post"
,
"w0tyes after"
,
"tweedle dum post"
,
"tweedle"
],
around
.
history
end
end
class
SkipCallbacksTest
<
Test
::
Unit
::
TestCase
def
test_skip_person
person
=
PersonSkipper
.
new
assert_equal
[],
person
.
history
person
.
save
assert_equal
[
[
:before_save
,
:string
],
[
:before_save
,
:proc
],
[
:before_save
,
:object
],
[
:before_save
,
:block
],
[
:after_save
,
:block
],
[
:after_save
,
:object
],
[
:after_save
,
:proc
],
[
:after_save
,
:string
],
[
:after_save
,
:symbol
]
],
person
.
history
end
end
class
CallbacksTest
<
Test
::
Unit
::
TestCase
def
test_save_person
person
=
Person
.
new
assert_equal
[],
person
.
history
person
.
save
assert_equal
[
[
:before_save
,
:symbol
],
[
:before_save
,
:string
],
[
:before_save
,
:proc
],
[
:before_save
,
:object
],
[
:before_save
,
:block
],
[
:after_save
,
:block
],
[
:after_save
,
:object
],
[
:after_save
,
:proc
],
[
:after_save
,
:string
],
[
:after_save
,
:symbol
]
],
person
.
history
end
end
class
ConditionalCallbackTest
<
Test
::
Unit
::
TestCase
def
test_save_conditional_person
person
=
ConditionalPerson
.
new
person
.
save
assert_equal
[
[
:before_save
,
:proc
],
[
:before_save
,
:proc
],
[
:before_save
,
:symbol
],
[
:before_save
,
:symbol
],
[
:before_save
,
:string
],
[
:before_save
,
:string
],
[
:before_save
,
:combined_symbol
],
],
person
.
history
end
end
class
ResetCallbackTest
<
Test
::
Unit
::
TestCase
def
test_save_conditional_person
person
=
CleanPerson
.
new
person
.
save
assert_equal
[],
person
.
history
end
end
class
CallbackTerminator
include
ActiveSupport
::
NewCallbacks
define_callbacks
:save
,
:terminator
=>
"result == :halt"
set_callback
:save
,
:before
,
:first
set_callback
:save
,
:before
,
:second
set_callback
:save
,
:around
,
:around_it
set_callback
:save
,
:before
,
:third
set_callback
:save
,
:after
,
:first
set_callback
:save
,
:around
,
:around_it
set_callback
:save
,
:after
,
:second
set_callback
:save
,
:around
,
:around_it
set_callback
:save
,
:after
,
:third
attr_reader
:history
,
:saved
def
initialize
@history
=
[]
end
def
around_it
@history
<<
"around1"
yield
@history
<<
"around2"
end
def
first
@history
<<
"first"
end
def
second
@history
<<
"second"
:halt
end
def
third
@history
<<
"third"
end
def
save
_run_save_callbacks
do
@saved
=
true
end
end
end
class
CallbackObject
def
before
(
caller
)
caller
.
record
<<
"before"
end
def
before_save
(
caller
)
caller
.
record
<<
"before save"
end
def
around
(
caller
)
caller
.
record
<<
"around before"
yield
caller
.
record
<<
"around after"
end
end
class
UsingObjectBefore
include
ActiveSupport
::
NewCallbacks
define_callbacks
:save
set_callback
:save
,
:before
,
CallbackObject
.
new
attr_accessor
:record
def
initialize
@record
=
[]
end
def
save
_run_save_callbacks
do
@record
<<
"yielded"
end
end
end
class
UsingObjectAround
include
ActiveSupport
::
NewCallbacks
define_callbacks
:save
set_callback
:save
,
:around
,
CallbackObject
.
new
attr_accessor
:record
def
initialize
@record
=
[]
end
def
save
_run_save_callbacks
do
@record
<<
"yielded"
end
end
end
class
CustomScopeObject
include
ActiveSupport
::
NewCallbacks
define_callbacks
:save
,
:scope
=>
[
:kind
,
:name
]
set_callback
:save
,
:before
,
CallbackObject
.
new
attr_accessor
:record
def
initialize
@record
=
[]
end
def
save
_run_save_callbacks
do
@record
<<
"yielded"
"CallbackResult"
end
end
end
class
UsingObjectTest
<
Test
::
Unit
::
TestCase
def
test_before_object
u
=
UsingObjectBefore
.
new
u
.
save
assert_equal
[
"before"
,
"yielded"
],
u
.
record
end
def
test_around_object
u
=
UsingObjectAround
.
new
u
.
save
assert_equal
[
"around before"
,
"yielded"
,
"around after"
],
u
.
record
end
def
test_customized_object
u
=
CustomScopeObject
.
new
u
.
save
assert_equal
[
"before save"
,
"yielded"
],
u
.
record
end
def
test_block_result_is_returned
u
=
CustomScopeObject
.
new
assert_equal
"CallbackResult"
,
u
.
save
end
end
class
CallbackTerminatorTest
<
Test
::
Unit
::
TestCase
def
test_termination
terminator
=
CallbackTerminator
.
new
terminator
.
save
assert_equal
[
"first"
,
"second"
,
"third"
,
"second"
,
"first"
],
terminator
.
history
end
def
test_block_never_called_if_terminated
obj
=
CallbackTerminator
.
new
obj
.
save
assert
!
obj
.
saved
end
end
class
HyphenatedKeyTest
<
Test
::
Unit
::
TestCase
def
test_save
obj
=
HyphenatedCallbacks
.
new
obj
.
save
assert_equal
obj
.
stuff
,
"OMG"
end
end
end
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录