Skip to content

Instantly share code, notes, and snippets.

@pocke
Created April 8, 2024 16:34
Show Gist options
  • Save pocke/ba106e74efe42b87c8cf071f112a7a97 to your computer and use it in GitHub Desktop.
Save pocke/ba106e74efe42b87c8cf071f112a7a97 to your computer and use it in GitHub Desktop.
Get core exts
module Monkey
extend self
MODULE_NAME = Module.instance_method(:name)
MODULE_SINGLETON_CLASS_P = Module.instance_method(:singleton_class?)
MODULE_SINGLETON_CLASS = Module.instance_method(:singleton_class)
MODULE_INSTANCE_METHODS = Module.instance_method(:instance_methods)
MODULE_INSTANCE_METHOD = Module.instance_method(:instance_method)
CLASS_ATTACHED_OBJECT = Class.instance_method(:attached_object) rescue nil
IS_A = Object.instance_method(:is_a?)
def diff
clean = clean_env
dirty = dirty_env
clean.each do |mod_key, methods|
mod, singleton_p = mod_key
d = dirty[mod_key]
methods.each do |method_name, clean_info|
dirty_info = d[method_name]
if clean_info[:source_location] != dirty_info[:source_location]
puts "#{mod}#{singleton_p ? '.singleton_class' : ''}##{method_name} has changed"
puts " clean: #{clean_info[:source_location]}"
puts " dirty: #{dirty_info[:source_location]}"
end
end
d.each do |method_name, dirty_info|
next if methods.key?(method_name)
puts "#{mod}#{singleton_p ? '.singleton_class' : ''}##{method_name} has been added"
puts " dirty: #{dirty_info[:source_location]}"
end
end
end
def clean_env
with_unbundled_env do
Marshal.load(`ruby --disable-gems -r #{File.expand_path(__FILE__)} -e 'puts Marshal.dump(Monkey.collect)'`)
end
end
def dirty_env
collect
end
def collect
h = {}
modules.each do |m|
next if ARGF.class == m
singleton_p = MODULE_SINGLETON_CLASS_P.bind_call(m)
if MODULE_NAME.bind_call(m) && !singleton_p
mod_key = [m, false]
elsif singleton_p && (a = attached_object(m)) && IS_A.bind_call(a, Class) && MODULE_NAME.bind_call(a)
mod_key = [a, true]
else
next
end
next if Monkey == mod_key[0]
h[mod_key] = {}
MODULE_INSTANCE_METHODS.bind_call(m).each do |method_name|
method = MODULE_INSTANCE_METHOD.bind_call(m, method_name)
h[mod_key][method_name] = {
source_location: method.source_location,
}
end
end
h
end
def attached_object(klass)
return CLASS_ATTACHED_OBJECT.bind_call(klass) if CLASS_ATTACHED_OBJECT
modules.find do |m|
IS_A.bind_call(m, Class) && MODULE_SINGLETON_CLASS.bind_call(m) == klass
end
end
def modules
@modules ||= ObjectSpace.each_object(Module).to_a
end
def with_unbundled_env(&block)
if defined?(::Bundler)
Bundler.with_unbundled_env(&block)
else
block.call
end
end
end
Monkey.diff if defined?(::Gem)
@pocke
Copy link
Author

pocke commented Apr 8, 2024

Requirement

No --disable-gems

Example

$ ruby -r YOUR_ENVIRONMENT -r ./monkey.rb

# For Rails
$ bin/rails r path/to/monkey.rb

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment