Get path of parent requiring file, Ruby - ruby

Is it possible to get the location of the file which requires another file in Ruby?
I have a project where I spawn some processes and I would love to be able, in the code, to determine which file is the parent of the required file. This is nice when debugging.
Example:
#initial.rb:
require "./my_file.rb"
fork do
require "./my_file2.rb"
end
-
#my_file.rb:
puts "Required from file: #{?????}"
-
#my_file2.rb:
require "./my_file.rb"
I would expect to get something like:
#=> Required from file: /path/to/initial.rb
#=> Required from file: /path/to/my_file2.rb

Based on Jacobs answer I ended with this redefinition of require_relative and require:
alias :old_require_relative :require_relative
def require_relative(arg)
#~ puts caller.map{|x| "\t#{x}"}
puts "%s requires %s" % [ caller.first.split(/:\d+/,2).first, arg]
old_require_relative arg
end
alias :old_require :require
def require(arg)
#~ puts caller.map{|x| "\t#{x}"}
puts "%s requires %s" % [ caller.first.split(/:\d+/,2).first, arg]
old_require arg
end
In a test test scenario with the following load sequence:
test.rb
+- test1.rb
+- test1_a.rb
+ test2.rb
The following calls
require './test1'
require './test2'
or
require_relative 'test1'
require_relative 'test2'
result in:
test.rb requires ./test1
C:/Temp/test1.rb requires test1_a
test.rb requires ./test2
You could also include the line of the requirement in the output.

You should never need to do this, but you can examine the call stack from Kernel#caller. You'll have to filter out require methods (especially if you use any libraries that override require).

Related

Accessing the PWD inside a thor task

In my main thor file, I call this code
script.rb
# this works
current_dir = Dir.getwd
# this changes directory into the tasks
Dir.chdir(#{pwd}/tasks) {
IO.popen("thor #{ARGV * ' '}") do |io|
while (line = io.gets) do
puts line
end
io.close
end
}
tasks/example.rb
require 'thor'
class Git < Thor
include Thor::Actions
desc 'test', 'test'
def test
puts Dir.getwd # this is showing my tasks folder
end
end
Inside example.rb How can I get access to the Dir.getwd value of the script.rb and not of the example.rb (this is wrong since it is running inside the Dir.chdir).
I tried global variables and such but it doesn't seem to be working.

How to call methods from different classes in ruby

Hi I am new to ruby and have recently started learning the same.I have written below ruby program which runs perfectly.
But this all program is written in a single file.I want to place each class in a different file and use one more file to execute all.ie like we do in java.[One main method to start the program] How can i run this program like this.
When i tried keeping each class in individual file and tried executing the same it started giving errors like method not found.
class Vehical
attr_accessor :odometer
attr_accessor :gas_used
def accelrate
puts " Floor It"
end
def sound_horn
puts "Beep! Beep!"
end
def steer
puts "Turn front 2 wheels"
end
def mileage
#odometer /#gas_used
end
end
class Truck < Vehical
end
class Motercycle < Vehical
end
class Car < Vehical
end
truck = Truck.new
truck.steer
car = Car.new
car.odometer = 11432
car.gas_used = 366
puts "Lifetime MPG:"
puts car.mileage
Below are few options:
Use require_relative to load those files into main file.
require_relative "vehical"
require_relative "truck"
require_relative "motor_cycle"
require_relative "car"
Use $:.unshift File.dirname(__FILE__) in main file to add its location to Ruby Load path, and use require "dependent-file-name-without-extension" in main file.
$:.unshift File.dirname(__FILE__)
require "vehical"
require "truck"
require "motor_cycle"
require "car"
Use -I <folder> when running the program. Example: ruby -I . main.rb
-Idirectory specify $LOAD_PATH directory (may be used more than once)

Spec Testing a Ruby CLI

I am trying to test the first ruby CLI i've written (n00b alert) and need some help. All my code is within 1 file, this includes a Class, OptionParser and some basic class execution methods. Here's an idea of what that looks like
The *rb. file
require 'optparse'
require 'fileutils'
class Foo
attr_accessor :arg, :opt
def initialize(p={})
#opt = p[:opt] || false
end
def do_something(arg)
#arg = arg
end
#more methods...
end
# Options
#options={}
#opt_parser = OptionParser.new do |opt|
opt.banner = "<{ FooBar }>"
opt.separator "------------"
opt.on("-o", "--opt", "An Option" do
#options[:opt] = true
end
end
#opt_parser.parse!
#CLI Execution
#foo = Foo.new(#options)
#foo.do_something(ARGV[0])
So here is the problem, i know would like to run some rspec tests rspec spec/ that i've wrote for the class, however the lines outside the class get executed of course and im left with an ARGV error.
What im looking for
Is there a better way to organize my code so i can test all the pieces, or how could i write a test to accommodate this file, Any and all suggestions would be greatly appreciated.
One posible solution is to wrap your option parsing code with a conditional that checks if the file is being run directly or loaded by some other file.
if __FILE__ == $0
# option parsing code
end
If you do that then all the code inside the if __FILE__ == $0 will not run with your test, but the rest of the code will run normally.

Three Ruby classes, more than three problems?

I have three Ruby files in the same directory:
classthree.rb
otherclass.rb
samplecode.rb
Here are the contents of classthree.rb:
require './samplecode.rb'
require './otherclass.rb'
class ClassThree
def initialize()
puts "this class three here"
end
end
Here are the contents of samplecode.rb:
require './otherclass.rb'
require './classthree.rb'
class SampleCode
$smart = SampleCode.new
#sides = 3
##x = "333"
def ugly()
g = ClassThree.new
puts g
puts "monkey see"
end
def self.ugly()
s = SampleCode.new
s.ugly
puts s
puts $smart
puts "monkey see this self"
end
SampleCode.ugly
end
Here are the contents of otherclass.rb:
require './samplecode.rb'
require './classthree.rb'
END {
puts "ending"
}
BEGIN{
puts "beginning"
}
class OtherClass
def initialize()
s = SampleCode.new
s.ugly
end
end
My two questions are:
There has to be a better way than require './xyz.rb' for every class in the directory. Isn't there something like require './*.rb'?
When I run ruby otherclass.rb I get the following output:
Why do I get "beginning" and "ending" twice each??
At 1 - The best way to deal with it is to create another file. You can call it environment.rb or initialize.rb, and it would require all the needed files.
$LOAD_PATH.unshift File.dirname(__FILE__)
require 'samplecode.rb'
require 'classthree.rb'
require 'classthree.rb'
Now you only need to require this file once on the start of the application.
At 2 - You started from file 'otherclass.rb'. It displays the first 'beginning' bit and then it loads samplecode.rb file. At this point, 'otherclass.rb' has not been loaded yet - it was not required by any other file. hence samplecode.rb is rerunning whole otherclass.rb, which is being required there. Rerunning doesn't reload 'samplecode.rb' as it was already required (require checks first whether file was or was not required). That's why you're seeing those messages twice.

Getting module of caller in Ruby

We have code to log data in our Ruby 1.8.6 web application. You call it roughly as follows:
$log.info("Some text here")
Now, in the logged output, I would like to include the module where that line appeared. I know that the Kernel#caller will give me an array where I can pull out the file and line number that the log line occurred, but I don't want that. I want the module, not the file name. The obvious solution is to modify the log line so that it reads like:
$log.info("Some text here", self.class.name)
and then parse the result. That's not going to work, though, because I am trying to extract this information in the default case. That is, I need the solution to work if the programmer forgot to specify the module, the second parameter to the log line.
Is there any way to do this? If not, I will just have to make do with the caller array; most of our modules are in separate directories, so this would be an 80% solution.
More complete example, please excuse minor syntax errors:
in file log.rb:
module Log
class Logger
def info(msg, mod = '')
puts "Module: #{mod} Msg: #{msg}"
end
end # class Logger
end # module Log
$log = Log::Logger.new
in file foo.rb:
module Foo
class Bar
def do_something
# Do not pass in self.class.name.
# We want the output to look like:
# Module: Foo Msg: I did something!
$log.info "I did something!"
end
end # class Bar
end #module Foo
Use call_stack.
First install it with RubyGems:
gem install call_stack
Then change log.rb to:
require 'rubygems'
require 'call_stack'
call_stack_on
module Log
class Logger
def info(msg, mod = '')
mod = call_stack(2)[0][0] if mod == ''
puts "Module: #{mod} Msg: #{msg}"
end
end # class Logger
end # module Log
$log = Log::Logger.new
Works for me (Ruby 1.8.7).
$ ruby foo.rb
Module: Foo::Bar Msg: I did something!
A mixin solves the OP's specific requirements (meanwhile, +1 to Asher for solving the generic "who called me" case!).
module Log
module Logger
def info(msg)
puts "Module: #{self} Msg: #{msg}"
end
end # class Logger
end # module Log
module Foo
class Bar
include Log::Logger
def do_something
# Do not pass in self.class.name.
# We want the output to look like:
# Module: Foo Msg: I did something!
info "I did something!"
end
end # class Bar
end # module Foo
foobar = Foo::Bar.new
foobar.do_something
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

Resources