How to determine where defined Ruby object? - ruby

Consider the following code in two files:
create_object.rb
def create_object
method = 'new'
Object.send(method)
end
debug.rb
require_relative './create_object'
def foo
object = create_object
bar(object)
end
def bar(object)
# history_of_life - some method that returns object info
puts object.history_of_life
end
foo
Run ruby debug.rb. I expect history_of_life method returns something like this:
<Object:0x007f874b083430> initialized in create_object.rb:3
Are there tools like pry-stacktrace to determine the place in the code where the Object was initialized?

It’s obviously off by default due to enormous footprint, but one might achieve more-or-less desired behaviour with ObjectSpace#allocation_class_path.
In general, ObjectSpace tweaks are your friends in tasks like this one on the top (ruby) abstraction level.

ObjectSpace.allocation_sourcefile returns the source file origin from the given object. Thanks for Aleksei Matiushkin advice.
debug.rb
require 'objspace'
require_relative './create_object'
def foo
object = create_object
bar(object)
end
def bar(object)
puts ObjectSpace.allocation_sourcefile(object)
end
ObjectSpace.trace_object_allocations do
foo
end
output
/home/user/pro/test/create_object.rb

Related

caller_method returns not the value, that i expected

I want to know, what method calls another method (I'm just trying to create simple expect("string").to eq("string") model (just like in RSpect, but more easier).
But i get "main", what is that? (I see that "main" for first time)
public
def expect(message)
message.to_s
end
def to
caller_method = caller_locations.first.label
puts caller_method
end
expect("test").to #=> <main>
#what output i expected:
expect("test").to #=> expect
My goal:
#first i need to do something like that:
expect("test").to eq("test") #=> true
#final must look like this:
expect(expect("test").to eq("test")).to eq(true) #=> true
I would recommend against using caller_method in this case. Rather, make a class whose methods return self - that way they will be chainable:
module Expectation
attr_accessor :caller_method
def expect(arg)
self.caller_method = "expect"
self
end
def to
caller_method
end
end
include Expectation
expect("foo").to
# => "expect"
Obviously this is only a starting point, and this doesn't actually do any comparisons / validations yet. But hopefully you can understand this pattern. The key thing is returning self to make a chainable API, and storing internal state using something like attr_accessor

Getting file name in ruby where method was called

I have a method foo and it is called in a script script001.rb
how should I write the foo method so that it returns the file name of a script that called it?
To avoid needing to deal with caller style strings, you can use Kernel#caller_locations, instead. It returns you an array of Thread::Backtrace::Location objects, which has some convenient methods available for you.
To get the filename, in your case, you can use the #path method:
def foo
caller_locations.first.path
end
You can use Kernel#caller which returns the current execution stack — an array containing strings in the form file:line or file:line: in 'method':
def foo
caller[0][/[^:]+/] # OR caller[0].split(':')[0]
end
#falsetru's answer is correct, but I thought I'd add this bit of code to demonstrate the different outputs of the methods proposed.
Two files.
hosting.rb
class Hosting
def self.foo
puts "__FILE__: #{__FILE__}"
puts "__method__: #{__method__}"
puts "caller: #{caller}"
puts "caller_locations.first.path: #{caller_locations.first.path}"
end
end
calling.rb
require_relative 'hosting'
Hosting.foo
On: ruby calling.rb the output is:
__FILE__: /path/to/hosting.rb
__method__: foo
caller: ["calling.rb:2:in `<main>'"]
caller_locations.first.path: calling.rb

Ruby: Is there a way to get the enclosing Module const of a Class?

I'm doing some metaprogramming in Ruby, and I need to dynamically generate a sibling class inside of a module. In doing so, I want to call const_set on the module, but I don't know which Module constant to call that on until runtime. An example:
Given classes
Foo::Bar::Baz
Foo::Quox::Quack
I want to be able to call a function like this (oversimplified here):
def generate_from klass
mod = klass.enclosing_module # <- THIS LINE is the one I need to figure out
mod.const_set("GeneratedClassName", Class.new)
end
and what I want to end up with, when calling with Baz, is a new class defined as
Foo::Bar::GeneratedClassName
and with a Quack, I want
Foo::Quox::GeneratedClassName
The only way I know of is to split up klass.name, then repeatedly call const_get on those strings, constantized. Does anyone know of a more elegant way?
This should get you on track:
module Foo
module Bar
class Baz
def initialize
#nesting = Module.nesting
end
def enclosing_module
#nesting.last
end
end
end
end
puts Foo::Bar::Baz.new.enclosing_module #=> Foo
Relevant documentation:
http://ruby-doc.org/core/classes/Module.html#M000441
Got it.
ActiveSupport has this Ruby extension, Module#parent. It's good enough for my use.
In Rails you can use a combination of deconstantize and constantize.
'Foo::Bar::Baz'.deconstantize.constantize # => Foo::Bar
so in a method of the class it can be used like this:
self.class.name.deconstantize.constantize
In case anyone is looking for a pure ruby version:
def get_parent_type
#Note: This will break for base types (lacking '::' in the name)
parent_type=self.class.name.split('::')[0...-1]
begin
Object.const_get(parent_type.join('::'))
rescue NameError => e
nil
end
end

How to retrieve caller context object in Ruby?

hereafter is my piece of code that I want to simplify in order to avoid passing an extra argument on each call. In fact, my usecase is that M is a user library without the definition of context argument on each method. check is a method that is not defined by the user.
# User code
module M
def do_something(context)
puts "Called from #{context}"
context.check
end
module_function :do_something
end
# Application code
class Bar
def check
puts "Checking from #{self}..."
end
end
class Foo < Bar
def do_stuff(scope, method)
scope.send method, self
end
end
# Executed by user
Foo.new.do_stuff M, :do_something
Is there a way to do the same think without passing self as an input argument to do_something method in order to retrieve check method ?
# User code
module M
def do_something
called_from_object = ???
puts "Called from #{called_from_object}"
called_from_object.check
end
module_function :do_something
end
# Application code
class Bar
def check
puts "Checking from #{self}..."
end
end
class Foo < Bar
def do_stuff(scope, method)
scope.send methood
end
end
# Executed by user
Foo.new.do_stuff M, :do_something
Thanks for your support!
Came across this post while looking for an answer for my own purposes.
Didn't find one that was appropriate, so I dug through the Ruby source and put together an extension. I've bundled it as a gem- should install without any problem so long as you are using Ruby 1.9.1:
sudo gem install sender
This will not work with Ruby 1.8, as 1.8 has a different model for tracking frames.
http://rubygems.org/gems/sender
Not what you're asking for, but if Foo were to include M would that allow you do achieve what you're after? e.g.
module M
def do_something
puts "I am going to use the test method from the including class"
test
end
end
class Foo
include M
def test
puts "In Foo's test method"
end
def do_stuff
do_something
end
end
and then you can do:
irb(main):019:0> Foo.new.do_stuff
I am going to use the test method from the including class
In Foo's test method
If the idea is to have a module provide some general functionality and have the specifics in a class then this is a fairly common pattern in ruby, e.g. the Comparable module requiring the including class to implement <=>.

How do I "fake" C# style attributes in Ruby?

EDIT: I slightly changed the spec, to better match what I imagined this to do.
Well, I don't really want to fake C# attributes, I want to one-up-them and support AOP as well.
Given the program:
class Object
def Object.profile
# magic code here
end
end
class Foo
# This is the fake attribute, it profiles a single method.
profile
def bar(b)
puts b
end
def barbar(b)
puts(b)
end
comment("this really should be fixed")
def snafu(b)
end
end
Foo.new.bar("test")
Foo.new.barbar("test")
puts Foo.get_comment(:snafu)
Desired output:
Foo.bar was called with param: b = "test"
test
Foo.bar call finished, duration was 1ms
test
This really should be fixed
Is there any way to achieve this?
I have a somewhat different approach:
class Object
def self.profile(method_name)
return_value = nil
time = Benchmark.measure do
return_value = yield
end
puts "#{method_name} finished in #{time.real}"
return_value
end
end
require "benchmark"
module Profiler
def method_added(name)
profile_method(name) if #method_profiled
super
end
def profile_method(method_name)
#method_profiled = nil
alias_method "unprofiled_#{method_name}", method_name
class_eval <<-ruby_eval
def #{method_name}(*args, &blk)
name = "\#{self.class}##{method_name}"
msg = "\#{name} was called with \#{args.inspect}"
msg << " and a block" if block_given?
puts msg
Object.profile(name) { unprofiled_#{method_name}(*args, &blk) }
end
ruby_eval
end
def profile
#method_profiled = true
end
end
module Comment
def method_added(name)
comment_method(name) if #method_commented
super
end
def comment_method(method_name)
comment = #method_commented
#method_commented = nil
alias_method "uncommented_#{method_name}", method_name
class_eval <<-ruby_eval
def #{method_name}(*args, &blk)
puts #{comment.inspect}
uncommented_#{method_name}(*args, &blk)
end
ruby_eval
end
def comment(text)
#method_commented = text
end
end
class Foo
extend Profiler
extend Comment
# This is the fake attribute, it profiles a single method.
profile
def bar(b)
puts b
end
def barbar(b)
puts(b)
end
comment("this really should be fixed")
def snafu(b)
end
end
A few points about this solution:
I provided the additional methods via modules which could be extended into new classes as needed. This avoids polluting the global namespace for all modules.
I avoided using alias_method, since module includes allow AOP-style extensions (in this case, for method_added) without the need for aliasing.
I chose to use class_eval rather than define_method to define the new method in order to be able to support methods that take blocks. This also necessitated the use of alias_method.
Because I chose to support blocks, I also added a bit of text to the output in case the method takes a block.
There are ways to get the actual parameter names, which would be closer to your original output, but they don't really fit in a response here. You can check out merb-action-args, where we wrote some code that required getting the actual parameter names. It works in JRuby, Ruby 1.8.x, Ruby 1.9.1 (with a gem), and Ruby 1.9 trunk (natively).
The basic technique here is to store a class instance variable when profile or comment is called, which is then applied when a method is added. As in the previous solution, the method_added hook is used to track when the new method is added, but instead of removing the hook each time, the hook checks for an instance variable. The instance variable is removed after the AOP is applied, so it only applies once. If this same technique was used multiple time, it could be further abstracted.
In general, I tried to stick as close to your "spec" as possible, which is why I included the Object.profile snippet instead of implementing it inline.
Great question. This is my quick attempt at an implementation (I did not try to optimise the code). I took the liberty of adding the profile method to the
Module class. In this way it will be available in every class and module definition. It would be even better
to extract it into a module and mix it into the class Module whenever you need it.
I also didn't know if the point was to make the profile method behave like Ruby's public/protected/private keywords,
but I implemented it like that anyway. All methods defined after calling profile are profiled, until noprofile is called.
class Module
def profile
require "benchmark"
#profiled_methods ||= []
class << self
# Save any original method_added callback.
alias_method :__unprofiling_method_added, :method_added
# Create new callback.
def method_added(method)
# Possible infinite loop if we do not check if we already replaced this method.
unless #profiled_methods.include?(method)
#profiled_methods << method
unbound_method = instance_method(method)
define_method(method) do |*args|
puts "#{self.class}##{method} was called with params #{args.join(", ")}"
bench = Benchmark.measure do
unbound_method.bind(self).call(*args)
end
puts "#{self.class}##{method} finished in %.5fs" % bench.real
end
# Call the original callback too.
__unprofiling_method_added(method)
end
end
end
end
def noprofile # What's the opposite of profile?
class << self
# Remove profiling callback and restore previous one.
alias_method :method_added, :__unprofiling_method_added
end
end
end
You can now use it as follows:
class Foo
def self.method_added(method) # This still works.
puts "Method '#{method}' has been added to '#{self}'."
end
profile
def foo(arg1, arg2, arg3 = nil)
puts "> body of foo"
sleep 1
end
def bar(arg)
puts "> body of bar"
end
noprofile
def baz(arg)
puts "> body of baz"
end
end
Call the methods as you would normally:
foo = Foo.new
foo.foo(1, 2, 3)
foo.bar(2)
foo.baz(3)
And get benchmarked output (and the result of the original method_added callback just to show that it still works):
Method 'foo' has been added to 'Foo'.
Method 'bar' has been added to 'Foo'.
Method 'baz' has been added to 'Foo'.
Foo#foo was called with params 1, 2, 3
> body of foo
Foo#foo finished in 1.00018s
Foo#bar was called with params 2
> body of bar
Foo#bar finished in 0.00016s
> body of baz
One thing to note is that it is impossible to dynamically get the name of the arguments with Ruby meta-programming.
You'd have to parse the original Ruby file, which is certainly possible but a little more complex. See the parse_tree and ruby_parser
gems for details.
A fun improvement would be to be able to define this kind of behaviour with a class method in the Module class. It would be cool to be able to do something like:
class Module
method_wrapper :profile do |*arguments|
# Do something before calling method.
yield *arguments # Call original method.
# Do something afterwards.
end
end
I'll leave this meta-meta-programming exercise for another time. :-)

Resources