class ActiveSupport::Callbacks::Callback

:nodoc:#

def _compile_filter(filter)


on the object.
a method is created that calls the before_foo method
Objects::
Procs:: define_method'ed into methods
Strings:: class_eval'ed into methods
Symbols:: Already methods
Arrays:: Merged together into a single filter

the same after this point:
All of these objects are compiled into methods and handled

Objects:: An object with a before_foo method on it to call
Procs:: A proc to call with the object
Strings:: Some content to evaluate
Symbols:: A method to call
merge conditions from skip_* filters
multiple conditions. Used internally to
Arrays:: Used in conditions. This is used to specify

Filters support:
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 _compile_options(options)

expression based on the options
symbols, string, procs, and objects), so compile a conditional
Options support the same options as filters themselves (and support
def _compile_options(options)
  conditions = ["true"]
  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
  conditions.flatten.join(" && ")
end

def _compile_per_key_options

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 if #{key_options}
    end
  RUBY_EVAL
end

def _normalize_legacy_filter(kind, filter)

def _normalize_legacy_filter(kind, filter)
  if !filter.respond_to?(kind) && filter.respond_to?(:filter)
    filter.singleton_class.class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
      def #{kind}(context, &block) filter(context, &block) end
    RUBY_EVAL
  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

def _update_filter(filter_options, new_options)

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 clone(chain, klass)

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 end(key=nil, object=nil)

before filters (for the backward pass).
This will supply contents for around and after filters, but not
def end(key=nil, object=nil)
  return if key && !object.send("_one_time_conditions_valid_#{@callback_id}?")
  case @kind
  when :after
    # after_save :filter_name, :if => :condition
    <<-RUBY_EVAL
    if #{@compiled_options}
      #{@filter}
    end
    RUBY_EVAL
  when :around
    <<-RUBY_EVAL
      value
    end
    RUBY_EVAL
  end
end

def initialize(chain, filter, kind, options, 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 matches?(_kind, _filter)

def matches?(_kind, _filter)
  @kind == _kind && @filter == _filter
end

def name

def name
  chain.name
end

def next_id

def next_id
  @@_callback_sequence += 1
end

def normalize_options!(options)

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 recompile!(_options, _per_key)

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 start(key=nil, object=nil)

contents for after filters (for the forward pass).
This will supply contents for before and around filters, and no
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
  #
  case @kind
  when :before
    # if condition    # before_save :filter_name, :if => :condition
    #   filter_name
    # end
    <<-RUBY_EVAL
      if !halted && #{@compiled_options}
        # This double assignment is to prevent warnings in 1.9.3 as
        # the `result` variable is not always used except if the
        # terminator code refers to it.
        result = result = #{@filter}
        halted = (#{chain.config[:terminator]})
        if halted
          halted_callback_hook(#{@raw_filter.inspect.inspect})
        end
      end
    RUBY_EVAL
  when :around
    # 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}"
    @klass.class_eval <<-RUBY_EVAL,  __FILE__, __LINE__ + 1
       def #{name}(halted)
        if #{@compiled_options} && !halted
          #{@filter} do
            yield self
          end
        else
          yield self
        end
      end
    RUBY_EVAL
    "#{name}(halted) do"
  end
end