How to include code from a file in Ruby? - ruby

I'm new to Ruby and I'm figuring out how to include a file in a script. I tried require and include but it doesn't work.
Here's the file I'd like to include, worth noting that it's not a module.
# file1.rb
if Constants.elementExist(driver, 'Allow') == true
allowElement = driver.find_element(:id, 'Allow')
allowElement.click()
sleep 1
wait.until {
driver.find_element(:id, 'Ok').click()
}
username = driver.find_element(:id, 'UsernameInput')
username.clear
username.send_keys Constants.itech_email
password = driver.find_element(:id, 'PasswordInput')
password.clear
password.send_keys Constants.itechPass
driver.find_element(:id, 'Login').click
else
username = driver.find_element(:id, 'UsernameInput')
username.clear
username.send_keys Constants.itech_email
password = driver.find_element(:id, 'PasswordInput')
password.clear
password.send_keys Constants.itechPass
driver.find_element(:id, 'Login').click
end
That file contains several lines of codes that are reusable or repeatable in my case. It's not inside a class or a module. It's a straightforward Ruby script, and I'd like to use this on my second script inside a module.
# file2.rb
module File2
module IOS
# include file1.rb
end
end
This way, it should just run the code of file1.rb inside file2.rb.
How can I do this in Ruby?

According to the Ruby docs, require has the following behavior:
If the filename does not resolve to an absolute path, it will be
searched for in the directories listed in $LOAD_PATH ($:).
Which means that in order to run file1.rb's code inside file2.rb, where both files are in the exact same folder, you'd have to do the following:
# file2.rb
module File2
module IOS
# Absolute path to file1.rb, adding '.rb' is optional
require './file1'
end
end

use require_relative:
require_relative 'file1.rb'

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.

Opening relative paths from gem

I'm writing a simple gem that can load from and save data to text files and zip archives. So, it has four methods: load_from_file, load_from_zip, save_to_file and save_to_zip respectfully. The problem is that I can't figure out how to specify relative paths for loading and saving for these methods. Here they go:
def load_from_file(filename)
File.open(filename) do |f|
f.each { |line| add(line) } # `add` is my another class method
end
end
def load_from_zip(filename)
Zip::File.open("#{filename}.zip") do |zipfile|
zipfile.each { |entry| read_zipped_file(zipfile, entry) } # my private method
end
end
def save_to_file(filename)
File.write("#{filename}.txt", data)
end
def save_to_zip(filename)
Zip::File.open("#{filename}.zip", Zip::File::CREATE) do |zipfile|
zipfile.get_output_stream('output.txt') { |f| f.print data }
end
end
private
def read_zipped_file(zipfile, entry)
zipfile.read(entry).lines.each { |line| add(line) }
end
So what I want basically is to allow this gem to load and save files by relative paths whereever it is used in system, e.g. I have an app located in /home/user/my_app with two files - app.rb and data.txt, and I could be able to read the file from this directory without specifying absolute path.
Example:
# app.rb
require 'my_gem'
my_gem = MyGem.new
my_gem.load_from_file('data.txt')
(Sorry for bad English)
UPD: This is not Rails gem and I'm not using Rails. All this is only pure Ruby.
Short answer
If I understand it correctly, you don't need to change anything.
Inside app.rb and your gem, relative paths will be understood relatively to Dir.pwd.
If you run ruby app.rb from inside /home/user/my_app :
Dir.pwd will be /home/user/my_app
both app.rb and my_gem will look for 'data.txt' inside /home/user/my_app.
Useful methods, just in case
Dir.chdir
If for some reason Dir.pwd isn't the desired folder, you could change directory :
Dir.chdir('/home/user/my_app') do
# relative paths will be based from /home/user/my_app
# call your gem from here
end
Get the directory of current file :
__dir__ will help you get the directory :
Returns the canonicalized absolute path of the directory of the file
from which this method is called.
Get the current file :
__FILE__ will return the current file. (Note : uppercase.)
Concatenate file paths :
If you need to concatenate file paths, use File.expand_path or File.join. Please don't concatenate strings.
If you don't trust that the relative path will be correctly resolved, you could send an absolute path to your method :
my_gem.load_from_file(File.expand_path('data.txt'))

Get path of parent requiring file, 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).

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.

config location ruby and using rake for unit test

Learning Ruby, my Ruby app directory structure follows the convention
with lib/ and test/
in my root directory I have a authentication config file, that I read from one of the classes in lib/. It is read as File.open('../myconf').
When testing with Rake, the file open doesn't work since the working directory is the root, not lib/ or test/.
In order to solve this, I have two questions:
is it possible, and should I specify rake working directory to test/ ?
should I use different file discovery method? Although I prefer convention over config.
lib/A.rb
class A
def openFile
if File.exists?('../auth.conf')
f = File.open('../auth.conf','r')
...
else
at_exit { puts "Missing auth.conf file" }
exit
end
end
test/testopenfile.rb
require_relative '../lib/A'
require 'test/unit'
class TestSetup < Test::Unit::TestCase
def test_credentials
a = A.new
a.openFile #error
...
end
end
Trying to invoke with Rake. I did setup a task to copy the auth.conf to the test directory, but turns out that the working dir is above test/.
> rake
cp auth.conf test/
/.../.rvm/rubies/ruby-1.9.3-p448/bin/ruby test/testsetup.rb
Missing auth.conf file
Rakefile
task :default => [:copyauth,:test]
desc "Copy auth.conf to test dir"
task :copyauth do
sh "cp auth.conf test/"
end
desc "Test"
task :test do
ruby "test/testsetup.rb"
end
You're probably getting that error because you're running rake from the projects root directory, which means that the current working directory will be set to that directory. This probably means that the call to File.open("../auth.conf") will start looking one directory up from your current working directory.
Try specifying the absolute path to the config file, for example something like this:
class A
def open_file
path = File.join(File.dirname(__FILE__), "..", "auth.conf")
if File.exists?(path)
f = File.open(path,'r')
# do stuff...
else
at_exit { puts "Missing auth.conf file" }
exit
end
end
Btw, I took the liberty of changing openFile -> open_file, since that's more consistent with ruby coding conventions.
I recommend using File.expand_path method for that. You can evaluate auth.conf file location based on __FILE__(current file - lib/a.rb in your case) or Rails.root depending on what you need.
def open_file
filename = File.expand_path("../auth.conf", __FILE__) # => 'lib/auth.conf'
if File.exists?(filename)
f = File.open(filename,'r')
...
else
at_exit { puts "Missing auth.conf file" }
exit
end
end

Resources