Skip to content

Instantly share code, notes, and snippets.

@alpaca-tc
Last active June 29, 2023 13:47
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save alpaca-tc/6d47f621658ca164a8d84884bc8eee50 to your computer and use it in GitHub Desktop.
Save alpaca-tc/6d47f621658ca164a8d84884bc8eee50 to your computer and use it in GitHub Desktop.
実際のスクリプトから少し削ってる。そのままだと動かないかも :gomenne:
# # ./bin/rails r script/associations_dumper.rb
dumper = MethodDumper.new do
# ブロックの中で全てのテストを実行する。
require "rspec"
require "rspec/core/formatters/documentation_formatter"
config = RSpec.configuration
formatter = RSpec::Core::Formatters::DocumentationFormatter.new(config.output_stream)
reporter = RSpec::Core::Reporter.new(config)
config.instance_variable_set(:@reporter, reporter)
loader = config.send(:formatter_loader)
notifications = loader.send(:notifications_for, RSpec::Core::Formatters::DocumentationFormatter)
reporter.register_listener(formatter, *notifications)
# 下記のようにオプションでパスを渡せば動作検証できる
# bundle exec rails runner __FILE__ -- spec/models/tenant_spec.rb
files = ARGV.presence || Dir[Rails.root.join("spec/**/*_spec.rb")].map { Pathname.new(_1).relative_path_from(Rails.root).to_s }
$LOAD_PATH.unshift(Rails.root.join("spec").to_s)
require "rails_helper"
RSpec::Core::Runner.run(files)
end
Rails.application.eager_load!
# 関連名を使っているメソッド群を解析対象とする
klasses = ApplicationRecord.descendants.reject(&:abstract_class?)
klasses.each do |klass|
klass.reflections.each do |reflection_name, reflection|
if reflection.belongs_to? || reflection.has_one?
dumper.add_method_trace("#{klass.generated_association_methods}#reload_#{reflection_name}")
dumper.add_method_trace("#{klass.generated_association_methods}#build_#{reflection_name}")
dumper.add_method_trace("#{klass.generated_association_methods}#create_#{reflection_name}")
dumper.add_method_trace("#{klass.generated_association_methods}#create_#{reflection_name}!")
end
dumper.add_method_trace("#{klass.generated_association_methods}##{reflection_name}")
dumper.add_method_trace("#{klass.generated_association_methods}##{reflection_name}=")
end
end
require "csv"
FileUtils.mkdir_p("tmp/dumps")
# rubocop:disable Rails/FilePath
backtrace_cleaner = ActiveSupport::BacktraceCleaner.new.tap do |cleaner|
cleaner.add_silencer { !(_1.start_with?("#{Rails.root}/app") || _1.start_with?("#{Rails.root}/spec")) }
end
# rubocop:enable Rails/FilePath
CSV.open("tmp/dumps/associations.csv", "w") do |csv|
headers = %i(klass method_name node_id)
csv << headers
dumper.run do |row|
trace_point = row[:trace_point]
backtrace = backtrace_cleaner.clean(trace_point.send(:caller))
called_from = backtrace[0].to_s
caller_location = trace_point.send(:caller_locations).find { _1.to_s == called_from }
node_id = RubyVM::AbstractSyntaxTree.node_id_for_backtrace_location(caller_location)
# 呼ばれた関連名メソッド
row = {
klass: row[:klass].to_s,
method_name: row[:method_name], # 呼び出したメソッド名
node_id:,
}
pp(row)
csv << headers.map { row.fetch(_1) }
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment