Ruby クラス・オブジェクト調査メソッド集 - nyg1971/test GitHub Wiki

Ruby クラス・オブジェクト調査メソッド集

基本的な調査メソッド

目的 メソッド 結果例
メソッド関連      
インスタンスメソッド一覧 Class.instance_methods(false) User.instance_methods(false) [:name, :email, :save_user]
クラスメソッド一覧 Class.methods(false) User.methods(false) [:find_by_email, :active]
メソッドの定義場所 obj.method(:name).source_location user.method(:save).source_location ["/path/file.rb", 10]
メソッドの存在確認 obj.respond_to?(:method) user.respond_to?(:email) true
クラス・継承関連      
継承チェーン Class.ancestors String.ancestors [String, Comparable, Object, ...]
混入モジュール Class.included_modules User.included_modules [ActiveRecord::Base, ...]
オブジェクトのクラス obj.class "hello".class String
型チェック obj.is_a?(Class) "hello".is_a?(String) true
変数・定数関連      
インスタンス変数 obj.instance_variables user.instance_variables [:@name, :@email]
クラス変数 Class.class_variables User.class_variables [:@@count]
定数一覧 Class.constants Math.constants [:PI, :E]

メソッド調査

基本的なメソッド確認

class User
  def instance_method; end
  def self.class_method; end
  private
  def private_method; end
end

# インスタンスメソッド(自分で定義したもののみ)
User.instance_methods(false)
# => [:instance_method]

# インスタンスメソッド(継承含む全て)
User.instance_methods(true)
# => [:instance_method, :class, :nil?, :===, :=~, ...]

# クラスメソッド(自分で定義したもののみ)
User.methods(false)
# => [:class_method]

# プライベートメソッドも含む
User.private_instance_methods(false)
# => [:private_method]

メソッドの詳細情報

user = User.new

# メソッドオブジェクトを取得
method_obj = user.method(:save)

# 定義場所の確認
method_obj.source_location
# => ["/path/to/activerecord/base.rb", 123]

# メソッドのarity(引数の個数)
method_obj.arity
# => -1 (可変引数の場合)

# メソッドの所有者(定義されているクラス/モジュール)
method_obj.owner
# => ActiveRecord::Base

# メソッドの存在確認
user.respond_to?(:save)
# => true

user.respond_to?(:save, true)  # プライベートメソッドも含む
# => true

パターンマッチングでメソッド検索

# 特定のパターンにマッチするメソッドを検索
User.instance_methods.grep(/save/)
# => [:save, :save!, :save_user]

# 大文字小文字を無視して検索
User.instance_methods.grep(/SAVE/i)
# => [:save, :save!]

# ActiveRecord関連のメソッドを検索
User.instance_methods.grep(/^find/)
# => [:find_by, :find_by!, :find_each, ...]

# 動的に生成されたメソッドの確認(Rails)
User.instance_methods.grep(/_attributes/)
# => [:nested_attributes_options, :posts_attributes=, ...]

クラス・継承構造の調査

継承チェーンの確認

# クラスの継承チェーン
String.ancestors
# => [String, Comparable, Object, Kernel, BasicObject]

# 直接の親クラスのみ
String.superclass
# => Object

# 継承しているか確認
String < Object
# => true

String <= String  # 自分自身も含む
# => true

モジュールの混入確認

class MyClass
  include Enumerable
  extend Forwardable
end

# 混入されているモジュール
MyClass.included_modules
# => [Enumerable, Kernel]

# 特定モジュールが混入されているか
MyClass.included_modules.include?(Enumerable)
# => true

# extend されたモジュールは特別な方法で確認
MyClass.singleton_class.included_modules
# => [Forwardable, ...]

オブジェクトの型確認

user = User.new

# 基本的な型確認
user.class
# => User

user.class.name
# => "User"

# 継承関係の確認
user.is_a?(User)
# => true

user.is_a?(ActiveRecord::Base)
# => true (Userが継承している場合)

user.kind_of?(User)  # is_a? の別名
# => true

# 厳密な型確認(継承関係を考慮しない)
user.instance_of?(User)
# => true

user.instance_of?(ActiveRecord::Base)
# => false

変数・定数の調査

インスタンス変数の調査

class User
  def initialize(name, email)
    @name = name
    @email = email
    @created_at = Time.now
  end
end

user = User.new("田中", "[email protected]")

# インスタンス変数一覧
user.instance_variables
# => [:@name, :@email, :@created_at]

# インスタンス変数の値取得
user.instance_variable_get(:@name)
# => "田中"

# インスタンス変数の値設定
user.instance_variable_set(:@age, 30)

# インスタンス変数が定義されているか
user.instance_variable_defined?(:@name)
# => true

クラス変数・定数の調査

class User
  @@count = 0
  MAX_USERS = 1000
  DEFAULT_ROLE = 'user'
end

# クラス変数一覧
User.class_variables
# => [:@@count]

# クラス変数の値取得
User.class_variable_get(:@@count)
# => 0

# 定数一覧
User.constants(false)  # 自分で定義したもののみ
# => [:MAX_USERS, :DEFAULT_ROLE]

User.constants(true)   # 継承したものも含む
# => [:MAX_USERS, :DEFAULT_ROLE, :ActiveSupport, :ActiveRecord, ...]

# 定数の値取得
User.const_get(:MAX_USERS)
# => 1000

# 定数が定義されているか
User.const_defined?(:MAX_USERS)
# => true

実用的な調査パターン

Rails モデルの動的メソッド調査

# ActiveRecordで動的生成されたメソッドを調査
def analyze_dynamic_methods(model_class)
  puts "=== #{model_class} Dynamic Methods ==="
  
  # enum関連のメソッド
  enum_methods = model_class.instance_methods.grep(/\?$|!$/).select do |method|
    model_class.defined_enums.keys.any? { |enum| method.to_s.include?(enum) }
  end
  puts "Enum methods: #{enum_methods}"
  
  # association関連のメソッド
  association_methods = model_class.instance_methods.grep(/_ids$|_attributes=$/)
  puts "Association methods: #{association_methods}"
  
  # scope関連のメソッド
  scope_methods = model_class.methods(false).grep(/^[a-z]/)
  puts "Possible scopes: #{scope_methods}"
end

analyze_dynamic_methods(User)

メソッドの定義場所マッピング

def method_location_map(obj, pattern = /.*/)
  methods = obj.methods.grep(pattern)
  
  methods.map do |method_name|
    location = obj.method(method_name).source_location
    [method_name, location]
  end.select { |_, location| location }.group_by { |_, location| location[0] }
end

# 使用例:saveに関連するメソッドの定義場所
method_location_map(User.new, /save/)

クラスの完全な構造分析

def class_analysis(klass)
  puts "=== #{klass} Analysis ==="
  puts "Superclass: #{klass.superclass}"
  puts "Ancestors: #{klass.ancestors.join(' -> ')}"
  puts ""
  
  puts "Own instance methods: #{klass.instance_methods(false).count}"
  puts "All instance methods: #{klass.instance_methods(true).count}"
  puts "Own class methods: #{klass.methods(false).count}"
  puts ""
  
  puts "Included modules:"
  klass.included_modules.each { |mod| puts "  #{mod}" }
  puts ""
  
  puts "Constants: #{klass.constants(false).count}"
  if klass.constants(false).any?
    puts "  #{klass.constants(false).join(', ')}"
  end
end

class_analysis(User)

メタプログラミング調査

動的メソッド定義の検出

# メソッドがどのように定義されたかを調査
def method_definition_analysis(klass, method_name)
  if klass.instance_methods(false).include?(method_name)
    method_obj = klass.instance_method(method_name)
    location = method_obj.source_location
    
    if location
      puts "#{method_name}: Explicitly defined at #{location}"
    else
      puts "#{method_name}: Dynamically generated (no source location)"
    end
  else
    puts "#{method_name}: Not found in #{klass}"
  end
end

# Rails の動的メソッドを調査
method_definition_analysis(User, :email)  # 通常の属性
method_definition_analysis(User, :active?) # enum メソッド

モジュールの混入方法の確認

def module_inclusion_analysis(klass)
  puts "=== #{klass} Module Inclusion ==="
  
  # include されたモジュール
  included = klass.included_modules - klass.superclass.included_modules
  puts "Included: #{included.join(', ')}" if included.any?
  
  # extend されたモジュール(singleton class に include)
  extended = klass.singleton_class.included_modules - 
             klass.superclass.singleton_class.included_modules
  puts "Extended: #{extended.join(', ')}" if extended.any?
  
  # prepend されたモジュール
  prepended = klass.ancestors.take_while { |ancestor| ancestor != klass }
  puts "Prepended: #{prepended.join(', ')}" if prepended.any?
end

module_inclusion_analysis(User)

デバッグ・トラブルシューティング

メソッド呼び出しのトレース

# メソッド呼び出しをトレース
trace = TracePoint.new(:call) do |tp|
  puts "#{tp.defined_class}##{tp.method_id} called"
end

trace.enable do
  user = User.new
  user.save
end

オブジェクトの状態確認

def object_state_analysis(obj)
  puts "=== Object State Analysis ==="
  puts "Class: #{obj.class}"
  puts "Object ID: #{obj.object_id}"
  puts "Frozen: #{obj.frozen?}"
  puts "Tainted: #{obj.tainted?}" if obj.respond_to?(:tainted?)
  
  puts "\nInstance variables:"
  obj.instance_variables.each do |var|
    value = obj.instance_variable_get(var)
    puts "  #{var}: #{value.inspect}"
  end
  
  puts "\nResponds to common methods:"
  [:save, :valid?, :errors, :id].each do |method|
    puts "  #{method}: #{obj.respond_to?(method)}"
  end
end

object_state_analysis(User.new)

未知のオブジェクトの調査

def unknown_object_analysis(obj)
  puts "=== Unknown Object Analysis ==="
  puts "Class: #{obj.class}"
  puts "Ancestors: #{obj.class.ancestors.first(5).join(' -> ')}..."
  
  puts "\nInteresting methods:"
  interesting_methods = obj.methods.grep(/^[a-z]/).reject do |method|
    [:class, :nil?, :respond_to?, :send, :object_id].include?(method)
  end.first(20)
  
  interesting_methods.each do |method|
    begin
      arity = obj.method(method).arity
      arity_info = arity == 0 ? "(no args)" : "(#{arity} args)"
      puts "  #{method} #{arity_info}"
    rescue
      puts "  #{method} (unknown arity)"
    end
  end
  
  puts "\nInstance variables:" if obj.instance_variables.any?
  obj.instance_variables.each do |var|
    puts "  #{var}"
  end
end

# 使用例
unknown_object_analysis(some_mysterious_object)

よく使うワンライナー集

# Rails モデルの動的メソッド一覧
User.instance_methods.grep(/_attributes|_ids/).sort

# 特定パターンのメソッドとその定義場所
User.instance_methods.grep(/save/).map { |m| [m, User.instance_method(m).source_location] }

# クラス階層の簡潔表示
User.ancestors.map(&:name).join(' -> ')

# ActiveRecord で生成されたメソッドの検出
User.instance_methods.select { |m| User.instance_method(m).source_location.nil? }

# モジュールの混入チェック
User.included_modules.map(&:name).grep(/Active/)

# 定数の値一覧
User.constants(false).map { |c| [c, User.const_get(c)] }.to_h

# インスタンス変数とその値
user.instance_variables.map { |v| [v, user.instance_variable_get(v)] }.to_h

# 引数なしメソッドの一覧
user.methods.select { |m| user.method(m).arity == 0 }.first(10)
⚠️ **GitHub.com Fallback** ⚠️