Skip to content

Instantly share code, notes, and snippets.

@yhirano55
Created June 3, 2019 14:56
Show Gist options
  • Save yhirano55/fcc420f981e8fe453d283a34b904f6b2 to your computer and use it in GitHub Desktop.
Save yhirano55/fcc420f981e8fe453d283a34b904f6b2 to your computer and use it in GitHub Desktop.

元コード

目的: どのようにバリデーションが定義されているかを知る

class Story < ApplicationRecord
  TraceLocation.trace(format: :markdown) do
    validates :title, allow_blank: true, length: { in: 1..65_535 }
  end
end

以下自動生成部分

Generated by trace_location at 2019-06-03 23:50:02 +0900

activemodel-5.2.3/lib/active_model/validations/validates.rb:105
ActiveModel::Validations::ClassMethods#validates
def validates(*attributes)
  defaults = attributes.extract_options!.dup
  validations = defaults.slice!(*_validates_default_keys)

  raise ArgumentError, "You need to supply at least one attribute" if attributes.empty?
  raise ArgumentError, "You need to supply at least one validation" if validations.empty?

  defaults[:attributes] = attributes

  validations.each do |key, options|
    next unless options
    key = "#{key.to_s.camelize}Validator"

    begin
      validator = key.include?("::".freeze) ? key.constantize : const_get(key)
    rescue NameError
      raise ArgumentError, "Unknown validator: '#{key}'"
    end

    validates_with(validator, defaults.merge(_parse_validates_options(options)))
  end
end
# called from /path/to/sample-app/app/models/story.rb:38
activesupport-5.2.3/lib/active_support/core_ext/array/extract_options.rb:24
Array#extract_options!
def extract_options!
  if last.is_a?(Hash) && last.extractable_options?
    pop
  else
    {}
  end
end
# called from activemodel-5.2.3/lib/active_model/validations/validates.rb:106
activesupport-5.2.3/lib/active_support/core_ext/array/extract_options.rb:9
Hash#extractable_options?
def extractable_options?
  instance_of?(Hash)
end
# called from activesupport-5.2.3/lib/active_support/core_ext/array/extract_options.rb:25
activemodel-5.2.3/lib/active_model/validations/validates.rb:156
ActiveModel::Validations::ClassMethods#_validates_default_keys
def _validates_default_keys
  [:if, :unless, :on, :allow_blank, :allow_nil, :strict]
end
# called from activemodel-5.2.3/lib/active_model/validations/validates.rb:107
activesupport-5.2.3/lib/active_support/core_ext/hash/slice.rb:32
Hash#slice!
def slice!(*keys)
  omit = slice(*self.keys - keys)
  hash = slice(*keys)
  hash.default      = default
  hash.default_proc = default_proc if default_proc
  replace(hash)
  omit
end
# called from activemodel-5.2.3/lib/active_model/validations/validates.rb:107
activesupport-5.2.3/lib/active_support/core_ext/string/inflections.rb:91
String#camelize
def camelize(first_letter = :upper)
  case first_letter
  when :upper
    ActiveSupport::Inflector.camelize(self, true)
  when :lower
    ActiveSupport::Inflector.camelize(self, false)
  else
    raise ArgumentError, "Invalid option, use either :upper or :lower."
  end
end
# called from activemodel-5.2.3/lib/active_model/validations/validates.rb:116
activesupport-5.2.3/lib/active_support/inflector/methods.rb:69
ActiveSupport::Inflector#camelize
def camelize(term, uppercase_first_letter = true)
  string = term.to_s
  if uppercase_first_letter
    string = string.sub(/^[a-z\d]*/) { |match| inflections.acronyms[match] || match.capitalize }
  else
    string = string.sub(inflections.acronyms_camelize_regex) { |match| match.downcase }
  end
  string.gsub!(/(?:_|(\/))([a-z\d]*)/i) { "#{$1}#{inflections.acronyms[$2] || $2.capitalize}" }
  string.gsub!("/".freeze, "::".freeze)
  string
end
# called from activesupport-5.2.3/lib/active_support/core_ext/string/inflections.rb:94
activesupport-5.2.3/lib/active_support/inflector/inflections.rb:252
ActiveSupport::Inflector#inflections
def inflections(locale = :en)
  if block_given?
    yield Inflections.instance(locale)
  else
    Inflections.instance(locale)
  end
end
# called from activesupport-5.2.3/lib/active_support/inflector/methods.rb:72
activesupport-5.2.3/lib/active_support/inflector/inflections.rb:66
ActiveSupport::Inflector::Inflections.instance
def self.instance(locale = :en)
  @__instance__[locale] ||= new
end
# called from activesupport-5.2.3/lib/active_support/inflector/inflections.rb:256
concurrent-ruby-1.1.5/lib/concurrent/map.rb:132
Concurrent::Map#[]
def [](key)
  if value = super # non-falsy value is an existing mapping, return it right away
    value
    # re-check is done with get_or_default(key, NULL) instead of a simple !key?(key) in order to avoid a race condition, whereby by the time the current thread gets to the key?(key) call
    # a key => value mapping might have already been created by a different thread (key?(key) would then return true, this elsif branch wouldn't be taken and an incorrent +nil+ value
    # would be returned)
    # note: nil == value check is not technically necessary
  elsif @default_proc && nil == value && NULL == (value = get_or_default(key, NULL))
    @default_proc.call(self, key)
  else
    value
  end
end
# called from activesupport-5.2.3/lib/active_support/inflector/inflections.rb:67
concurrent-ruby-1.1.5/lib/concurrent/collection/map/non_concurrent_map_backend.rb:19
Concurrent::Collection::NonConcurrentMapBackend#[]
def [](key)
  @backend[key]
end
# called from concurrent-ruby-1.1.5/lib/concurrent/map.rb:133
activemodel-5.2.3/lib/active_model/validations/validates.rb:160
ActiveModel::Validations::ClassMethods#_parse_validates_options
def _parse_validates_options(options)
  case options
  when TrueClass
    {}
  when Hash
    options
  when Range, Array
    { in: options }
  else
    { with: options }
  end
end
# called from activemodel-5.2.3/lib/active_model/validations/validates.rb:124
activemodel-5.2.3/lib/active_model/validations/with.rb:81
ActiveModel::Validations::ClassMethods#validates_with
def validates_with(*args, &block)
  options = args.extract_options!
  options[:class] = self

  args.each do |klass|
    validator = klass.new(options, &block)

    if validator.respond_to?(:attributes) && !validator.attributes.empty?
      validator.attributes.each do |attribute|
        _validators[attribute.to_sym] << validator
      end
    else
      _validators[nil] << validator
    end

    validate(validator, options)
  end
end
# called from activemodel-5.2.3/lib/active_model/validations/validates.rb:124
activesupport-5.2.3/lib/active_support/core_ext/array/extract_options.rb:24
Array#extract_options!
def extract_options!
  if last.is_a?(Hash) && last.extractable_options?
    pop
  else
    {}
  end
end
# called from activemodel-5.2.3/lib/active_model/validations/with.rb:82
activesupport-5.2.3/lib/active_support/core_ext/array/extract_options.rb:9
Hash#extractable_options?
def extractable_options?
  instance_of?(Hash)
end
# called from activesupport-5.2.3/lib/active_support/core_ext/array/extract_options.rb:25
activemodel-5.2.3/lib/active_model/validations/length.rb:11
ActiveModel::Validations::LengthValidator#initialize
def initialize(options)
  if range = (options.delete(:in) || options.delete(:within))
    raise ArgumentError, ":in and :within must be a Range" unless range.is_a?(Range)
    options[:minimum], options[:maximum] = range.min, range.max
  end

  if options[:allow_blank] == false && options[:minimum].nil? && options[:is].nil?
    options[:minimum] = 1
  end

  super
end
# called from activemodel-5.2.3/lib/active_model/validations/with.rb:86
activemodel-5.2.3/lib/active_model/validator.rb:138
ActiveModel::EachValidator#initialize
def initialize(options)
  @attributes = Array(options.delete(:attributes))
  raise ArgumentError, ":attributes cannot be blank" if @attributes.empty?
  super
  check_validity!
end
# called from activemodel-5.2.3/lib/active_model/validations/length.rb:21
activemodel-5.2.3/lib/active_model/validator.rb:108
ActiveModel::Validator#initialize
def initialize(options = {})
  @options = options.except(:class).freeze
end
# called from activemodel-5.2.3/lib/active_model/validator.rb:141
activesupport-5.2.3/lib/active_support/core_ext/hash/except.rb:12
Hash#except
def except(*keys)
  dup.except!(*keys)
end
# called from activemodel-5.2.3/lib/active_model/validator.rb:109
activesupport-5.2.3/lib/active_support/core_ext/hash/except.rb:20
Hash#except!
def except!(*keys)
  keys.each { |key| delete(key) }
  self
end
# called from activesupport-5.2.3/lib/active_support/core_ext/hash/except.rb:13
activemodel-5.2.3/lib/active_model/validations/length.rb:24
ActiveModel::Validations::LengthValidator#check_validity!
def check_validity!
  keys = CHECKS.keys & options.keys

  if keys.empty?
    raise ArgumentError, "Range unspecified. Specify the :in, :within, :maximum, :minimum, or :is option."
  end

  keys.each do |key|
    value = options[key]

    unless (value.is_a?(Integer) && value >= 0) || value == Float::INFINITY || value.is_a?(Symbol) || value.is_a?(Proc)
      raise ArgumentError, ":#{key} must be a nonnegative Integer, Infinity, Symbol, or Proc"
    end
  end
end
# called from activemodel-5.2.3/lib/active_model/validator.rb:142
activesupport-5.2.3/lib/active_support/core_ext/class/attribute.rb:106
Story._validators
redefine_method(name) { val }
# called from activemodel-5.2.3/lib/active_model/validations/with.rb:90
activemodel-5.2.3/lib/active_model/validations.rb:154
ActiveModel::Validations::ClassMethods#validate
def validate(*args, &block)
  options = args.extract_options!

  if args.all? { |arg| arg.is_a?(Symbol) }
    options.each_key do |k|
      unless VALID_OPTIONS_FOR_VALIDATE.include?(k)
        raise ArgumentError.new("Unknown key: #{k.inspect}. Valid keys are: #{VALID_OPTIONS_FOR_VALIDATE.map(&:inspect).join(', ')}. Perhaps you meant to call `validates` instead of `validate`?")
      end
    end
  end

  if options.key?(:on)
    options = options.dup
    options[:on] = Array(options[:on])
    options[:if] = Array(options[:if])
    options[:if].unshift ->(o) {
      !(options[:on] & Array(o.validation_context)).empty?
    }
  end

  set_callback(:validate, *args, options, &block)
end
# called from activemodel-5.2.3/lib/active_model/validations/with.rb:96
activesupport-5.2.3/lib/active_support/core_ext/array/extract_options.rb:24
Array#extract_options!
def extract_options!
  if last.is_a?(Hash) && last.extractable_options?
    pop
  else
    {}
  end
end
# called from activemodel-5.2.3/lib/active_model/validations.rb:155
activesupport-5.2.3/lib/active_support/core_ext/array/extract_options.rb:9
Hash#extractable_options?
def extractable_options?
  instance_of?(Hash)
end
# called from activesupport-5.2.3/lib/active_support/core_ext/array/extract_options.rb:25
activesupport-5.2.3/lib/active_support/callbacks.rb:667
ActiveSupport::Callbacks::ClassMethods#set_callback
def set_callback(name, *filter_list, &block)
  type, filters, options = normalize_callback_params(filter_list, block)

  self_chain = get_callbacks name
  mapped = filters.map do |filter|
    Callback.build(self_chain, filter, type, options)
  end

  __update_callbacks(name) do |target, chain|
    options[:prepend] ? chain.prepend(*mapped) : chain.append(*mapped)
    target.set_callbacks name, chain
  end
end
# called from activemodel-5.2.3/lib/active_model/validations.rb:174
activesupport-5.2.3/lib/active_support/callbacks.rb:615
ActiveSupport::Callbacks::ClassMethods#normalize_callback_params
def normalize_callback_params(filters, block) # :nodoc:
  type = CALLBACK_FILTER_TYPES.include?(filters.first) ? filters.shift : :before
  options = filters.extract_options!
  filters.unshift(block) if block
  [type, filters, options.dup]
end
# called from activesupport-5.2.3/lib/active_support/callbacks.rb:668
activesupport-5.2.3/lib/active_support/core_ext/array/extract_options.rb:24
Array#extract_options!
def extract_options!
  if last.is_a?(Hash) && last.extractable_options?
    pop
  else
    {}
  end
end
# called from activesupport-5.2.3/lib/active_support/callbacks.rb:617
activesupport-5.2.3/lib/active_support/core_ext/array/extract_options.rb:9
Hash#extractable_options?
def extractable_options?
  instance_of?(Hash)
end
# called from activesupport-5.2.3/lib/active_support/core_ext/array/extract_options.rb:25
activesupport-5.2.3/lib/active_support/callbacks.rb:836
ActiveSupport::Callbacks::ClassMethods#get_callbacks
def get_callbacks(name) # :nodoc:
  __callbacks[name.to_sym]
end
# called from activesupport-5.2.3/lib/active_support/callbacks.rb:670
activesupport-5.2.3/lib/active_support/core_ext/class/attribute.rb:106
Story.__callbacks
redefine_method(name) { val }
# called from activesupport-5.2.3/lib/active_support/callbacks.rb:837
activesupport-5.2.3/lib/active_support/callbacks.rb:281
ActiveSupport::Callbacks::Callback.build
def self.build(chain, filter, kind, options)
  if filter.is_a?(String)
    raise ArgumentError, <<-MSG.squish
      Passing string to define a callback is not supported. See the `.set_callback`
      documentation to see supported values.
    MSG
  end

  new chain.name, filter, kind, options, chain.config
end
# called from activesupport-5.2.3/lib/active_support/callbacks.rb:672
activesupport-5.2.3/lib/active_support/callbacks.rb:295
ActiveSupport::Callbacks::Callback#initialize
def initialize(name, filter, kind, options, chain_config)
  @chain_config = chain_config
  @name    = name
  @kind    = kind
  @filter  = filter
  @key     = compute_identifier filter
  @if      = check_conditionals(Array(options[:if]))
  @unless  = check_conditionals(Array(options[:unless]))
end
# called from activesupport-5.2.3/lib/active_support/callbacks.rb:289
activesupport-5.2.3/lib/active_support/callbacks.rb:365
ActiveSupport::Callbacks::Callback#compute_identifier
def compute_identifier(filter)
  case filter
  when ::Proc
    filter.object_id
  else
    filter
  end
end
# called from activesupport-5.2.3/lib/active_support/callbacks.rb:300
activesupport-5.2.3/lib/active_support/callbacks.rb:353
ActiveSupport::Callbacks::Callback#check_conditionals
def check_conditionals(conditionals)
  if conditionals.any? { |c| c.is_a?(String) }
    raise ArgumentError, <<-MSG.squish
      Passing string to be evaluated in :if and :unless conditional
      options is not supported. Pass a symbol for an instance method,
      or a lambda, proc or block, instead.
    MSG
  end

  conditionals
end
# called from activesupport-5.2.3/lib/active_support/callbacks.rb:301
activesupport-5.2.3/lib/active_support/callbacks.rb:353
ActiveSupport::Callbacks::Callback#check_conditionals
def check_conditionals(conditionals)
  if conditionals.any? { |c| c.is_a?(String) }
    raise ArgumentError, <<-MSG.squish
      Passing string to be evaluated in :if and :unless conditional
      options is not supported. Pass a symbol for an instance method,
      or a lambda, proc or block, instead.
    MSG
  end

  conditionals
end
# called from activesupport-5.2.3/lib/active_support/callbacks.rb:302
activesupport-5.2.3/lib/active_support/callbacks.rb:624
ActiveSupport::Callbacks::ClassMethods#__update_callbacks
def __update_callbacks(name) #:nodoc:
  ([self] + ActiveSupport::DescendantsTracker.descendants(self)).reverse_each do |target|
    chain = target.get_callbacks name
    yield target, chain.dup
  end
end
# called from activesupport-5.2.3/lib/active_support/callbacks.rb:675
activesupport-5.2.3/lib/active_support/descendants_tracker.rb:14
ActiveSupport::DescendantsTracker.descendants
def descendants(klass)
  arr = []
  accumulate_descendants(klass, arr)
  arr
end
# called from activesupport-5.2.3/lib/active_support/callbacks.rb:625
activesupport-5.2.3/lib/active_support/descendants_tracker.rb:41
ActiveSupport::DescendantsTracker.accumulate_descendants
def accumulate_descendants(klass, acc)
  if direct_descendants = @@direct_descendants[klass]
    acc.concat(direct_descendants)
    direct_descendants.each { |direct_descendant| accumulate_descendants(direct_descendant, acc) }
  end
end
# called from activesupport-5.2.3/lib/active_support/descendants_tracker.rb:16
activesupport-5.2.3/lib/active_support/callbacks.rb:836
ActiveSupport::Callbacks::ClassMethods#get_callbacks
def get_callbacks(name) # :nodoc:
  __callbacks[name.to_sym]
end
# called from activesupport-5.2.3/lib/active_support/callbacks.rb:626
activesupport-5.2.3/lib/active_support/core_ext/class/attribute.rb:106
Story.__callbacks
redefine_method(name) { val }
# called from activesupport-5.2.3/lib/active_support/callbacks.rb:837
activesupport-5.2.3/lib/active_support/callbacks.rb:557
ActiveSupport::Callbacks::CallbackChain#initialize_copy
def initialize_copy(other)
  @callbacks = nil
  @chain     = other.chain.dup
  @mutex     = Mutex.new
end
# called from activesupport-5.2.3/lib/active_support/callbacks.rb:627
activesupport-5.2.3/lib/active_support/callbacks.rb:581
ActiveSupport::Callbacks::CallbackChain#chain
def chain; @chain; end
# called from activesupport-5.2.3/lib/active_support/callbacks.rb:559
activesupport-5.2.3/lib/active_support/callbacks.rb:572
ActiveSupport::Callbacks::CallbackChain#append
def append(*callbacks)
  callbacks.each { |c| append_one(c) }
end
# called from activesupport-5.2.3/lib/active_support/callbacks.rb:676
activesupport-5.2.3/lib/active_support/callbacks.rb:585
ActiveSupport::Callbacks::CallbackChain#append_one
def append_one(callback)
  @callbacks = nil
  remove_duplicates(callback)
  @chain.push(callback)
end
# called from activesupport-5.2.3/lib/active_support/callbacks.rb:573
activesupport-5.2.3/lib/active_support/callbacks.rb:597
ActiveSupport::Callbacks::CallbackChain#remove_duplicates
def remove_duplicates(callback)
  @callbacks = nil
  @chain.delete_if { |c| callback.duplicates?(c) }
end
# called from activesupport-5.2.3/lib/active_support/callbacks.rb:587
activesupport-5.2.3/lib/active_support/callbacks.rb:324
ActiveSupport::Callbacks::Callback#duplicates?
def duplicates?(other)
  case @filter
  when Symbol
    matches?(other.kind, other.filter)
  else
    false
  end
end
# called from activesupport-5.2.3/lib/active_support/callbacks.rb:599
activesupport-5.2.3/lib/active_support/callbacks.rb:840
ActiveSupport::Callbacks::ClassMethods#set_callbacks
def set_callbacks(name, callbacks) # :nodoc:
  self.__callbacks = __callbacks.merge(name.to_sym => callbacks)
end
# called from activesupport-5.2.3/lib/active_support/callbacks.rb:677
activesupport-5.2.3/lib/active_support/core_ext/class/attribute.rb:106
Story.__callbacks
redefine_method(name) { val }
# called from activesupport-5.2.3/lib/active_support/callbacks.rb:841
activesupport-5.2.3/lib/active_support/core_ext/class/attribute.rb:104
ActiveRecord::Base.__callbacks=
define_singleton_method("#{name}=") do |val|
  singleton_class.class_eval do
    redefine_method(name) { val }
  end

  if singleton_class?
    class_eval do
      redefine_method(name) do
        if instance_variable_defined? ivar
          instance_variable_get ivar
        else
          singleton_class.send name
        end
      end
    end
  end
  val
end
# called from activesupport-5.2.3/lib/active_support/callbacks.rb:841
activesupport-5.2.3/lib/active_support/core_ext/module/redefine_method.rb:26
Module#redefine_method
def redefine_method(method, &block)
  visibility = method_visibility(method)
  silence_redefinition_of_method(method)
  define_method(method, &block)
  send(visibility, method)
end
# called from activesupport-5.2.3/lib/active_support/core_ext/class/attribute.rb:106
activesupport-5.2.3/lib/active_support/core_ext/module/redefine_method.rb:39
Module#method_visibility
def method_visibility(method) # :nodoc:
  case
  when private_method_defined?(method)
    :private
  when protected_method_defined?(method)
    :protected
  else
    :public
  end
end
# called from activesupport-5.2.3/lib/active_support/core_ext/module/redefine_method.rb:27
activesupport-5.2.3/lib/active_support/core_ext/module/redefine_method.rb:8
Module#silence_redefinition_of_method
def silence_redefinition_of_method(method)
  if method_defined?(method) || private_method_defined?(method)
    # This suppresses the "method redefined" warning; the self-alias
    # looks odd, but means we don't need to generate a unique name
    alias_method method, method
  end
end
# called from activesupport-5.2.3/lib/active_support/core_ext/module/redefine_method.rb:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment