Advertisement
Guest User

Untitled

a guest
Jan 16th, 2017
124
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Ruby 26.04 KB | None | 0 0
  1. require 'active_support/concern'
  2. require 'active_support/descendants_tracker'
  3. require 'active_support/core_ext/array/extract_options'
  4. require 'active_support/core_ext/class/attribute'
  5. require 'active_support/core_ext/kernel/reporting'
  6. require 'active_support/core_ext/kernel/singleton_class'
  7. require 'active_support/core_ext/module/attribute_accessors'
  8. require 'active_support/core_ext/string/filters'
  9. require 'active_support/deprecation'
  10. require 'thread'
  11.  
  12. module ActiveSupport
  13.   # Callbacks are code hooks that are run at key points in an object's life cycle.
  14.   # The typical use case is to have a base class define a set of callbacks
  15.   # relevant to the other functionality it supplies, so that subclasses can
  16.   # install callbacks that enhance or modify the base functionality without
  17.   # needing to override or redefine methods of the base class.
  18.   #
  19.   # Mixing in this module allows you to define the events in the object's
  20.   # life cycle that will support callbacks (via +ClassMethods.define_callbacks+),
  21.   # set the instance methods, procs, or callback objects to be called (via
  22.   # +ClassMethods.set_callback+), and run the installed callbacks at the
  23.   # appropriate times (via +run_callbacks+).
  24.   #
  25.   # Three kinds of callbacks are supported: before callbacks, run before a
  26.   # certain event; after callbacks, run after the event; and around callbacks,
  27.   # blocks that surround the event, triggering it when they yield. Callback code
  28.   # can be contained in instance methods, procs or lambdas, or callback objects
  29.   # that respond to certain predetermined methods. See +ClassMethods.set_callback+
  30.   # for details.
  31.   #
  32.   #   class Record
  33.   #     include ActiveSupport::Callbacks
  34.   #     define_callbacks :save
  35.   #
  36.   #     def save
  37.   #       run_callbacks :save do
  38.   #         puts "- save"
  39.   #       end
  40.   #     end
  41.   #   end
  42.   #
  43.   #   class PersonRecord < Record
  44.   #     set_callback :save, :before, :saving_message
  45.   #     def saving_message
  46.   #       puts "saving..."
  47.   #     end
  48.   #
  49.   #     set_callback :save, :after do |object|
  50.   #       puts "saved"
  51.   #     end
  52.   #   end
  53.   #
  54.   #   person = PersonRecord.new
  55.   #   person.save
  56.   #
  57.   # Output:
  58.   #   saving...
  59.   #   - save
  60.   #   saved
  61.   module Callbacks
  62.     extend Concern
  63.  
  64.     included do
  65.       extend ActiveSupport::DescendantsTracker
  66.     end
  67.  
  68.     CALLBACK_FILTER_TYPES = [:before, :after, :around]
  69.  
  70.     # If true, Active Record and Active Model callbacks returning +false+ will
  71.     # halt the entire callback chain and display a deprecation message.
  72.     # If false, callback chains will only be halted by calling +throw :abort+.
  73.     # Defaults to +true+.
  74.     mattr_accessor(:halt_and_display_warning_on_return_false, instance_writer: false) { true }
  75.  
  76.     # Runs the callbacks for the given event.
  77.     #
  78.     # Calls the before and around callbacks in the order they were set, yields
  79.     # the block (if given one), and then runs the after callbacks in reverse
  80.     # order.
  81.     #
  82.     # If the callback chain was halted, returns +false+. Otherwise returns the
  83.     # result of the block, +nil+ if no callbacks have been set, or +true+
  84.     # if callbacks have been set but no block is given.
  85.     #
  86.     #   run_callbacks :save do
  87.     #     save
  88.     #   end
  89.     def run_callbacks(kind, &block)
  90.       send "_run_#{kind}_callbacks", &block
  91.     end
  92.  
  93.     private
  94.  
  95.     def __run_callbacks__(callbacks, &block)
  96.       if callbacks.empty?
  97.         yield if block_given?
  98.       else
  99.         runner = callbacks.compile
  100.         e = Filters::Environment.new(self, false, nil, block)
  101.         runner.call(e).value
  102.       end
  103.     end
  104.  
  105.     # A hook invoked every time a before callback is halted.
  106.     # This can be overridden in AS::Callback implementors in order
  107.     # to provide better debugging/logging.
  108.     def halted_callback_hook(filter)
  109.     end
  110.  
  111.     module Conditionals # :nodoc:
  112.       class Value
  113.         def initialize(&block)
  114.           @block = block
  115.         end
  116.         def call(target, value); @block.call(value); end
  117.       end
  118.     end
  119.  
  120.     module Filters
  121.       Environment = Struct.new(:target, :halted, :value, :run_block)
  122.  
  123.       class End
  124.         def call(env)
  125.           block = env.run_block
  126.           env.value = !env.halted && (!block || block.call)
  127.           env
  128.         end
  129.       end
  130.       ENDING = End.new
  131.  
  132.       class Before
  133.         def self.build(callback_sequence, user_callback, user_conditions, chain_config, filter)
  134.           halted_lambda = chain_config[:terminator]
  135.  
  136.           if user_conditions.any?
  137.             halting_and_conditional(callback_sequence, user_callback, user_conditions, halted_lambda, filter)
  138.           else
  139.             halting(callback_sequence, user_callback, halted_lambda, filter)
  140.           end
  141.         end
  142.  
  143.         def self.halting_and_conditional(callback_sequence, user_callback, user_conditions, halted_lambda, filter)
  144.           callback_sequence.before do |env|
  145.             target = env.target
  146.             value  = env.value
  147.             halted = env.halted
  148.  
  149.             if !halted && user_conditions.all? { |c| c.call(target, value) }
  150.               result_lambda = -> { user_callback.call target, value }
  151.               env.halted = halted_lambda.call(target, result_lambda)
  152.               if env.halted
  153.                 target.send :halted_callback_hook, filter
  154.               end
  155.             end
  156.  
  157.             env
  158.           end
  159.         end
  160.         private_class_method :halting_and_conditional
  161.  
  162.         def self.halting(callback_sequence, user_callback, halted_lambda, filter)
  163.           callback_sequence.before do |env|
  164.             target = env.target
  165.             value  = env.value
  166.             halted = env.halted
  167.  
  168.             unless halted
  169.               result_lambda = -> { user_callback.call target, value }
  170.               env.halted = halted_lambda.call(target, result_lambda)
  171.  
  172.               if env.halted
  173.                 target.send :halted_callback_hook, filter
  174.               end
  175.             end
  176.  
  177.             env
  178.           end
  179.         end
  180.         private_class_method :halting
  181.       end
  182.  
  183.       class After
  184.         def self.build(callback_sequence, user_callback, user_conditions, chain_config)
  185.           if chain_config[:skip_after_callbacks_if_terminated]
  186.             if user_conditions.any?
  187.               halting_and_conditional(callback_sequence, user_callback, user_conditions)
  188.             else
  189.               halting(callback_sequence, user_callback)
  190.             end
  191.           else
  192.             if user_conditions.any?
  193.               conditional callback_sequence, user_callback, user_conditions
  194.             else
  195.               simple callback_sequence, user_callback
  196.             end
  197.           end
  198.         end
  199.  
  200.         def self.halting_and_conditional(callback_sequence, user_callback, user_conditions)
  201.           callback_sequence.after do |env|
  202.             target = env.target
  203.             value  = env.value
  204.             halted = env.halted
  205.  
  206.             if !halted && user_conditions.all? { |c| c.call(target, value) }
  207.               user_callback.call target, value
  208.             end
  209.  
  210.             env
  211.           end
  212.         end
  213.         private_class_method :halting_and_conditional
  214.  
  215.         def self.halting(callback_sequence, user_callback)
  216.           callback_sequence.after do |env|
  217.             unless env.halted
  218.               user_callback.call env.target, env.value
  219.             end
  220.  
  221.             env
  222.           end
  223.         end
  224.         private_class_method :halting
  225.  
  226.         def self.conditional(callback_sequence, user_callback, user_conditions)
  227.           callback_sequence.after do |env|
  228.             target = env.target
  229.             value  = env.value
  230.  
  231.             if user_conditions.all? { |c| c.call(target, value) }
  232.               user_callback.call target, value
  233.             end
  234.  
  235.             env
  236.           end
  237.         end
  238.         private_class_method :conditional
  239.  
  240.         def self.simple(callback_sequence, user_callback)
  241.           callback_sequence.after do |env|
  242.             user_callback.call env.target, env.value
  243.  
  244.             env
  245.           end
  246.         end
  247.         private_class_method :simple
  248.       end
  249.  
  250.       class Around
  251.         def self.build(callback_sequence, user_callback, user_conditions, chain_config)
  252.           if user_conditions.any?
  253.             halting_and_conditional(callback_sequence, user_callback, user_conditions)
  254.           else
  255.             halting(callback_sequence, user_callback)
  256.           end
  257.         end
  258.  
  259.         def self.halting_and_conditional(callback_sequence, user_callback, user_conditions)
  260.           callback_sequence.around do |env, &run|
  261.             target = env.target
  262.             value  = env.value
  263.             halted = env.halted
  264.  
  265.             if !halted && user_conditions.all? { |c| c.call(target, value) }
  266.               user_callback.call(target, value) {
  267.                 run.call.value
  268.               }
  269.               env
  270.             else
  271.               run.call
  272.             end
  273.           end
  274.         end
  275.         private_class_method :halting_and_conditional
  276.  
  277.         def self.halting(callback_sequence, user_callback)
  278.           callback_sequence.around do |env, &run|
  279.             target = env.target
  280.             value  = env.value
  281.  
  282.             if env.halted
  283.               run.call
  284.             else
  285.               user_callback.call(target, value) {
  286.                 run.call.value
  287.               }
  288.               env
  289.             end
  290.           end
  291.         end
  292.         private_class_method :halting
  293.       end
  294.     end
  295.  
  296.     class Callback #:nodoc:#
  297.       def self.build(chain, filter, kind, options)
  298.         if filter.is_a?(String)
  299.           ActiveSupport::Deprecation.warn(<<-MSG.squish)
  300.             Passing string to define callback is deprecated and will be removed
  301.             in Rails 5.1 without replacement.
  302.           MSG
  303.         end
  304.  
  305.         new chain.name, filter, kind, options, chain.config
  306.       end
  307.  
  308.       attr_accessor :kind, :name
  309.       attr_reader :chain_config
  310.  
  311.       def initialize(name, filter, kind, options, chain_config)
  312.         @chain_config  = chain_config
  313.         @name    = name
  314.         @kind    = kind
  315.         @filter  = filter
  316.         @key     = compute_identifier filter
  317.         @if      = Array(options[:if])
  318.         @unless  = Array(options[:unless])
  319.       end
  320.  
  321.       def filter; @key; end
  322.       def raw_filter; @filter; end
  323.  
  324.       def merge_conditional_options(chain, if_option:, unless_option:)
  325.         options = {
  326.           :if     => @if.dup,
  327.           :unless => @unless.dup
  328.         }
  329.  
  330.         options[:if].concat     Array(unless_option)
  331.         options[:unless].concat Array(if_option)
  332.  
  333.         self.class.build chain, @filter, @kind, options
  334.       end
  335.  
  336.       def matches?(_kind, _filter)
  337.         @kind == _kind && filter == _filter
  338.       end
  339.  
  340.       def duplicates?(other)
  341.         case @filter
  342.         when Symbol, String
  343.           matches?(other.kind, other.filter)
  344.         else
  345.           false
  346.         end
  347.       end
  348.  
  349.       # Wraps code with filter
  350.       def apply(callback_sequence)
  351.         user_conditions = conditions_lambdas
  352.         user_callback = make_lambda @filter
  353.  
  354.         case kind
  355.         when :before
  356.           Filters::Before.build(callback_sequence, user_callback, user_conditions, chain_config, @filter)
  357.         when :after
  358.           Filters::After.build(callback_sequence, user_callback, user_conditions, chain_config)
  359.         when :around
  360.           Filters::Around.build(callback_sequence, user_callback, user_conditions, chain_config)
  361.         end
  362.       end
  363.  
  364.       private
  365.  
  366.       def invert_lambda(l)
  367.         lambda { |*args, &blk| !l.call(*args, &blk) }
  368.       end
  369.  
  370.       # Filters support:
  371.       #
  372.       #   Symbols:: A method to call.
  373.       #   Strings:: Some content to evaluate.
  374.       #   Procs::   A proc to call with the object.
  375.       #   Objects:: An object with a <tt>before_foo</tt> method on it to call.
  376.       #
  377.       # All of these objects are converted into a lambda and handled
  378.       # the same after this point.
  379.       def make_lambda(filter)
  380.         case filter
  381.         when Symbol
  382.           lambda { |target, _, &blk| target.send filter, &blk }
  383.         when String
  384.           l = eval "lambda { |value| #{filter} }"
  385.           lambda { |target, value| target.instance_exec(value, &l) }
  386.         when Conditionals::Value then filter
  387.         when ::Proc
  388.           if filter.arity > 1
  389.             return lambda { |target, _, &block|
  390.               raise ArgumentError unless block
  391.               target.instance_exec(target, block, &filter)
  392.             }
  393.           end
  394.  
  395.           if filter.arity <= 0
  396.             lambda { |target, _| target.instance_exec(&filter) }
  397.           else
  398.             lambda { |target, _| target.instance_exec(target, &filter) }
  399.           end
  400.         else
  401.           scopes = Array(chain_config[:scope])
  402.           method_to_call = scopes.map{ |s| public_send(s) }.join("_")
  403.  
  404.           lambda { |target, _, &blk|
  405.             filter.public_send method_to_call, target, &blk
  406.           }
  407.         end
  408.       end
  409.      
  410.       #ReceiverName: ActiveSupport::Callbacks::Callback
  411.       #GemName: activesupport
  412.       #GemVersion: 5.0.1
  413.       #Visibility: PRIVATE
  414.       # Proc -> Fixnum
  415.       # Module -> Module
  416.       # Symbol -> Symbol
  417.       # ActiveSupport::ExecutionWrapper::RunHook -> ActiveSupport::ExecutionWrapper::RunHook
  418.       # ActiveSupport::ExecutionWrapper::CompleteHook -> ActiveSupport::ExecutionWrapper::CompleteHook
  419.       def compute_identifier(filter)
  420.         case filter
  421.         when String, ::Proc
  422.           filter.object_id
  423.         else
  424.           filter
  425.         end
  426.       end
  427.  
  428.       def conditions_lambdas
  429.         @if.map { |c| make_lambda c } +
  430.           @unless.map { |c| invert_lambda make_lambda c }
  431.       end
  432.     end
  433.  
  434.     # Execute before and after filters in a sequence instead of
  435.     # chaining them with nested lambda calls, see:
  436.     # https://github.com/rails/rails/issues/18011
  437.     class CallbackSequence
  438.       def initialize(&call)
  439.         @call = call
  440.         @before = []
  441.         @after = []
  442.       end
  443.  
  444.       def before(&before)
  445.         @before.unshift(before)
  446.         self
  447.       end
  448.  
  449.       def after(&after)
  450.         @after.push(after)
  451.         self
  452.       end
  453.  
  454.       def around(&around)
  455.         CallbackSequence.new do |arg|
  456.           around.call(arg) {
  457.             self.call(arg)
  458.           }
  459.         end
  460.       end
  461.  
  462.       def call(arg)
  463.         @before.each { |b| b.call(arg) }
  464.         value = @call.call(arg)
  465.         @after.each { |a| a.call(arg) }
  466.         value
  467.       end
  468.     end
  469.  
  470.     # An Array with a compile method.
  471.     class CallbackChain #:nodoc:#
  472.       include Enumerable
  473.  
  474.       attr_reader :name, :config
  475.  
  476.       def initialize(name, config)
  477.         @name = name
  478.         @config = {
  479.           scope: [:kind],
  480.           terminator: default_terminator
  481.         }.merge!(config)
  482.         @chain = []
  483.         @callbacks = nil
  484.         @mutex = Mutex.new
  485.       end
  486.  
  487.       def each(&block); @chain.each(&block); end
  488.       def index(o);     @chain.index(o); end
  489.       def empty?;       @chain.empty?; end
  490.  
  491.       def insert(index, o)
  492.         @callbacks = nil
  493.         @chain.insert(index, o)
  494.       end
  495.  
  496.       def delete(o)
  497.         @callbacks = nil
  498.         @chain.delete(o)
  499.       end
  500.  
  501.       def clear
  502.         @callbacks = nil
  503.         @chain.clear
  504.         self
  505.       end
  506.  
  507.       def initialize_copy(other)
  508.         @callbacks = nil
  509.         @chain     = other.chain.dup
  510.         @mutex     = Mutex.new
  511.       end
  512.  
  513.       def compile
  514.         @callbacks || @mutex.synchronize do
  515.           final_sequence = CallbackSequence.new { |env| Filters::ENDING.call(env) }
  516.           @callbacks ||= @chain.reverse.inject(final_sequence) do |callback_sequence, callback|
  517.             callback.apply callback_sequence
  518.           end
  519.         end
  520.       end
  521.  
  522.       def append(*callbacks)
  523.         callbacks.each { |c| append_one(c) }
  524.       end
  525.  
  526.       def prepend(*callbacks)
  527.         callbacks.each { |c| prepend_one(c) }
  528.       end
  529.  
  530.       protected
  531.       def chain; @chain; end
  532.  
  533.       private
  534.  
  535.       def append_one(callback)
  536.         @callbacks = nil
  537.         remove_duplicates(callback)
  538.         @chain.push(callback)
  539.       end
  540.  
  541.       def prepend_one(callback)
  542.         @callbacks = nil
  543.         remove_duplicates(callback)
  544.         @chain.unshift(callback)
  545.       end
  546.  
  547.       def remove_duplicates(callback)
  548.         @callbacks = nil
  549.         @chain.delete_if { |c| callback.duplicates?(c) }
  550.       end
  551.  
  552.       def default_terminator
  553.         Proc.new do |target, result_lambda|
  554.           terminate = true
  555.           catch(:abort) do
  556.             result_lambda.call if result_lambda.is_a?(Proc)
  557.             terminate = false
  558.           end
  559.           terminate
  560.         end
  561.       end
  562.     end
  563.  
  564.     module ClassMethods
  565.       def normalize_callback_params(filters, block) # :nodoc:
  566.         type = CALLBACK_FILTER_TYPES.include?(filters.first) ? filters.shift : :before
  567.         options = filters.extract_options!
  568.         filters.unshift(block) if block
  569.         [type, filters, options.dup]
  570.       end
  571.  
  572.       # This is used internally to append, prepend and skip callbacks to the
  573.       # CallbackChain.
  574.       def __update_callbacks(name) #:nodoc:
  575.         ([self] + ActiveSupport::DescendantsTracker.descendants(self)).reverse_each do |target|
  576.           chain = target.get_callbacks name
  577.           yield target, chain.dup
  578.         end
  579.       end
  580.  
  581.       # Install a callback for the given event.
  582.       #
  583.       #   set_callback :save, :before, :before_method
  584.       #   set_callback :save, :after,  :after_method, if: :condition
  585.       #   set_callback :save, :around, ->(r, block) { stuff; result = block.call; stuff }
  586.       #
  587.       # The second argument indicates whether the callback is to be run +:before+,
  588.       # +:after+, or +:around+ the event. If omitted, +:before+ is assumed. This
  589.       # means the first example above can also be written as:
  590.       #
  591.       #   set_callback :save, :before_method
  592.       #
  593.       # The callback can be specified as a symbol naming an instance method; as a
  594.       # proc, lambda, or block; as a string to be instance evaluated(deprecated); or as an
  595.       # object that responds to a certain method determined by the <tt>:scope</tt>
  596.       # argument to +define_callbacks+.
  597.       #
  598.       # If a proc, lambda, or block is given, its body is evaluated in the context
  599.       # of the current object. It can also optionally accept the current object as
  600.       # an argument.
  601.       #
  602.       # Before and around callbacks are called in the order that they are set;
  603.       # after callbacks are called in the reverse order.
  604.       #
  605.       # Around callbacks can access the return value from the event, if it
  606.       # wasn't halted, from the +yield+ call.
  607.       #
  608.       # ===== Options
  609.       #
  610.       # * <tt>:if</tt> - A symbol, a string or an array of symbols and strings,
  611.       #   each naming an instance method or a proc; the callback will be called
  612.       #   only when they all return a true value.
  613.       # * <tt>:unless</tt> - A symbol, a string or an array of symbols and
  614.       #   strings, each naming an instance method or a proc; the callback will
  615.       #   be called only when they all return a false value.
  616.       # * <tt>:prepend</tt> - If +true+, the callback will be prepended to the
  617.       #   existing chain rather than appended.
  618.       def set_callback(name, *filter_list, &block)
  619.         type, filters, options = normalize_callback_params(filter_list, block)
  620.         self_chain = get_callbacks name
  621.         mapped = filters.map do |filter|
  622.           Callback.build(self_chain, filter, type, options)
  623.         end
  624.  
  625.         __update_callbacks(name) do |target, chain|
  626.           options[:prepend] ? chain.prepend(*mapped) : chain.append(*mapped)
  627.           target.set_callbacks name, chain
  628.         end
  629.       end
  630.  
  631.       # Skip a previously set callback. Like +set_callback+, <tt>:if</tt> or
  632.       # <tt>:unless</tt> options may be passed in order to control when the
  633.       # callback is skipped.
  634.       #
  635.       #   class Writer < Person
  636.       #      skip_callback :validate, :before, :check_membership, if: -> { self.age > 18 }
  637.       #   end
  638.       #
  639.       # An <tt>ArgumentError</tt> will be raised if the callback has not
  640.       # already been set (unless the <tt>:raise</tt> option is set to <tt>false</tt>).
  641.       def skip_callback(name, *filter_list, &block)
  642.         type, filters, options = normalize_callback_params(filter_list, block)
  643.         options[:raise] = true unless options.key?(:raise)
  644.  
  645.         __update_callbacks(name) do |target, chain|
  646.           filters.each do |filter|
  647.             callback = chain.find {|c| c.matches?(type, filter) }
  648.  
  649.             if !callback && options[:raise]
  650.               raise ArgumentError, "#{type.to_s.capitalize} #{name} callback #{filter.inspect} has not been defined"
  651.             end
  652.  
  653.             if callback && (options.key?(:if) || options.key?(:unless))
  654.               new_callback = callback.merge_conditional_options(chain, if_option: options[:if], unless_option: options[:unless])
  655.               chain.insert(chain.index(callback), new_callback)
  656.             end
  657.  
  658.             chain.delete(callback)
  659.           end
  660.           target.set_callbacks name, chain
  661.         end
  662.       end
  663.  
  664.       # Remove all set callbacks for the given event.
  665.       def reset_callbacks(name)
  666.         callbacks = get_callbacks name
  667.  
  668.         ActiveSupport::DescendantsTracker.descendants(self).each do |target|
  669.           chain = target.get_callbacks(name).dup
  670.           callbacks.each { |c| chain.delete(c) }
  671.           target.set_callbacks name, chain
  672.         end
  673.  
  674.         self.set_callbacks name, callbacks.dup.clear
  675.       end
  676.  
  677.       # Define sets of events in the object life cycle that support callbacks.
  678.       #
  679.       #   define_callbacks :validate
  680.       #   define_callbacks :initialize, :save, :destroy
  681.       #
  682.       # ===== Options
  683.       #
  684.       # * <tt>:terminator</tt> - Determines when a before filter will halt the
  685.       #   callback chain, preventing following before and around callbacks from
  686.       #   being called and the event from being triggered.
  687.       #   This should be a lambda to be executed.
  688.       #   The current object and the result lambda of the callback will be provided
  689.       #   to the terminator lambda.
  690.       #
  691.       #     define_callbacks :validate, terminator: ->(target, result_lambda) { result_lambda.call == false }
  692.       #
  693.       #   In this example, if any before validate callbacks returns +false+,
  694.       #   any successive before and around callback is not executed.
  695.       #
  696.       #   The default terminator halts the chain when a callback throws +:abort+.
  697.       #
  698.       # * <tt>:skip_after_callbacks_if_terminated</tt> - Determines if after
  699.       #   callbacks should be terminated by the <tt>:terminator</tt> option. By
  700.       #   default after callbacks are executed no matter if callback chain was
  701.       #   terminated or not. This option makes sense only when <tt>:terminator</tt>
  702.       #   option is specified.
  703.       #
  704.       # * <tt>:scope</tt> - Indicates which methods should be executed when an
  705.       #   object is used as a callback.
  706.       #
  707.       #     class Audit
  708.       #       def before(caller)
  709.       #         puts 'Audit: before is called'
  710.       #       end
  711.       #
  712.       #       def before_save(caller)
  713.       #         puts 'Audit: before_save is called'
  714.       #       end
  715.       #     end
  716.       #
  717.       #     class Account
  718.       #       include ActiveSupport::Callbacks
  719.       #
  720.       #       define_callbacks :save
  721.       #       set_callback :save, :before, Audit.new
  722.       #
  723.       #       def save
  724.       #         run_callbacks :save do
  725.       #           puts 'save in main'
  726.       #         end
  727.       #       end
  728.       #     end
  729.       #
  730.       #   In the above case whenever you save an account the method
  731.       #   <tt>Audit#before</tt> will be called. On the other hand
  732.       #
  733.       #     define_callbacks :save, scope: [:kind, :name]
  734.       #
  735.       #   would trigger <tt>Audit#before_save</tt> instead. That's constructed
  736.       #   by calling <tt>#{kind}_#{name}</tt> on the given instance. In this
  737.       #   case "kind" is "before" and "name" is "save". In this context +:kind+
  738.       #   and +:name+ have special meanings: +:kind+ refers to the kind of
  739.       #   callback (before/after/around) and +:name+ refers to the method on
  740.       #   which callbacks are being defined.
  741.       #
  742.       #   A declaration like
  743.       #
  744.       #     define_callbacks :save, scope: [:name]
  745.       #
  746.       #   would call <tt>Audit#save</tt>.
  747.       #
  748.       # NOTE: +method_name+ passed to `define_model_callbacks` must not end with
  749.       # `!`, `?` or `=`.
  750.       def define_callbacks(*names)
  751.         options = names.extract_options!
  752.  
  753.         names.each do |name|
  754.           class_attribute "_#{name}_callbacks", instance_writer: false
  755.           set_callbacks name, CallbackChain.new(name, options)
  756.  
  757.           module_eval <<-RUBY, __FILE__, __LINE__ + 1
  758.             def _run_#{name}_callbacks(&block)
  759.               __run_callbacks__(_#{name}_callbacks, &block)
  760.             end
  761.           RUBY
  762.         end
  763.       end
  764.  
  765.       protected
  766.  
  767.       def get_callbacks(name) # :nodoc:
  768.         send "_#{name}_callbacks"
  769.       end
  770.  
  771.       def set_callbacks(name, callbacks) # :nodoc:
  772.         send "_#{name}_callbacks=", callbacks
  773.       end
  774.  
  775.       def deprecated_false_terminator # :nodoc:
  776.         Proc.new do |target, result_lambda|
  777.           terminate = true
  778.           catch(:abort) do
  779.             result = result_lambda.call if result_lambda.is_a?(Proc)
  780.             if Callbacks.halt_and_display_warning_on_return_false && result == false
  781.               display_deprecation_warning_for_false_terminator
  782.             else
  783.               terminate = false
  784.             end
  785.           end
  786.           terminate
  787.         end
  788.       end
  789.  
  790.       private
  791.  
  792.       def display_deprecation_warning_for_false_terminator
  793.         ActiveSupport::Deprecation.warn(<<-MSG.squish)
  794.           Returning `false` in Active Record and Active Model callbacks will not implicitly halt a callback chain in Rails 5.1.
  795.           To explicitly halt the callback chain, please use `throw :abort` instead.
  796.         MSG
  797.       end
  798.     end
  799.   end
  800. end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement