Call EXCEL method from Module - ruby

Creating module:
module CF
def work_sheet(excel_doc_name, workbook_name)
dir_to_excel = Dir.pwd + '/lib/config/data/'
read_workbook = RubyXL::Parser.parse("#{dir_to_excel}#{excel_doc_name}")
worksheet = read_workbook["#{workbook_name}"]
end
end
Call this method from module:
Given(/^Excel read$/) do
include CF
work_sheet("Login.xlsx", "Login_info")
end
Error message:
NoMethodError: undefined method `work_sheet' for #<Object:0x442f158>
Please check

To include the methods of a module in a step definition, you need to add the module to the World.
In your env.rb (or a file required by it) add the following line (after you have required the module file):
World(CF)
You should also remove the include CF line from the step definition.

Related

Ruby include module helper inside class - :cannot load such file

I've pure Ruby app with HashHelper located in lib/hash_helpers.rb which I want to use inside Matcher class in lib/matcher.rb.
To fire Matcher class I've got separate file in root directory of the app called runner.rb. How to include HashHelpers to be used inside the class Matcher ?
What I did is:
# runner.rb
require_relative 'lib/matcher'
matched_words = Matcher.new('dictionary.txt').call
p matched_words
# lib/matcher.rb
require_relative 'hash_helpers'
class Matcher
(...)
end
# lib/hash_helpers.rb
module HashHelpers
extend self
(...)
end
When I tried to run ruby runner.rb I'm getting an error:
lib/monads_matcher.rb:6:in `require_relative': cannot load such file -- /Users/usr/dictionary_app/lib/hash_helpers (LoadError)

How do I extend a Ruby class inside a module?

I'm trying to extend the File class inside a module. Here's my (abbreviated) code:
module Wireshark
# Extend the file class to write a header comment
class File
def write_header
self.puts '/* ' + Wireshark::AUTOGEN_LABEL + ' */'
self.puts '/* ' + Wireshark::timestamp + ' */'
end
end
# Read a file
def read
begin
file = File.read 'file'
rescue IOError
STDERR.puts 'Error reading file.'
return
end
end
end
When I run my code, I'm getting
undefined method `read' for Wireshark::File:Class (NoMethodError)
when I try to run file.read. I tried getting rid of the module encapsulation, but I'd like to only extend the File class inside my module, not in the rest of my program.
You're close.
module Wireshark
module File
def write_header
self.puts '/* ' + Wireshark::AUTOGEN_LABEL + ' */'
self.puts '/* ' + Wireshark::timestamp + ' */'
end
end
# Extend File with the methods in the Wireshark::File module
::File.send :include, Wireshark::File
# Read a file
def read
begin
file = ::File.read 'file'
rescue IOError
STDERR.puts 'Error reading file.'
return
end
end
end
The general idea here is that we define a Wireshark::File module that holds the methods you want to include on the File class, then you can just include them on File directly.
You'll also notice that in the read method, I changed File to ::File. Ruby will walk up the tree to try to find the nearest matching value for a given constant, so since you are in the Wireshark module, and there is a constant named File in that scope, using just File gets you Wireshark::File. Specifying ::File means "The File constant at the top-level namespace".
In the current version of Ruby it's not possible to do this, but it is a popular proposed extension called "refinements".
If you need to patch the core File class, you'll need to do that as a monkey patch, and this affects all instances of File anywhere in your Ruby process.
Normally you can do it this way:
# Define the instance methods you want to overide
module MyFileHacksInstanceMethods
def read
# ... (reimplementation) ...
end
end
# Force load these in the File class
class File
include MyFileHacksInstanceMethods
end
What you've declared in your example here is an independent class called Wireshark::File because it's within that module's namespace. ::File is the main class (:: being a prefix to force absolute name, sort of like / for filesystems).

Ruby: Include a dynamic module name

I have a situation in my Rails application where I need to include arbitrary modules depending on the current runtime state. The module provides custom application code that is only needed when certain conditions are true. Basically, I'm pulling the name of a company from the current context and using that as the filename for the module and its definition:
p = self.user.company.subdomain + ".rb"
if File.exists?(Rails.root + "lib/" + p)
include self.class.const_get(self.user.company.subdomain.capitalize.to_sym)
self.custom_add_url
end
My test module looks like this:
module Companyx
def custom_add_url
puts "Calling custom_add_url"
end
end
Now in the console, this actually works fine. I can pull a user and include the module like so:
[1] pry(main)> c = Card.find_by_personal_url("username")
[2] pry(main)> include c.class.const_get(c.user.company.subdomain.capitalize)=> Object
[3] pry(main)> c.custom_add_url
Calling custom_add_url
If I try to run the include line from my model, I get
NoMethodError: undefined method `include' for #<Card:0x007f91f9094fb0>
Can anyone suggest why the include statement would work on the console, but not in my model code?
I'm doing a similar thing. I found this answer useful:
How to convert a string to a constant in Ruby?
Turns out I was looking for the constantize method. This is the line I'm using in my code:
include "ModuleName::#{var.attr}".constantize
Edit:
So ack, I ran into various problems with actually using that line myself. Partially because I was trying to call it inside a method in a class. But since I'm only calling one method in the class (which calls/runs everything else) the final working version I have now is
"ModuleName::#{var.attr}".constantize.new.methodname
Obviously methodname is an instance method, so you could get rid of the new if yours is a class method.
Include is a method on a class.
If you want to call it inside a model, you need to execute the code in the context of its singleton class.
p = self.user.company.subdomain + ".rb"
if File.exists?(Rails.root + "lib/" + p)
myself = self
class_eval do
include self.const_get(myself.user.company.subdomain.capitalize.to_sym)
end
self.custom_add_url
EDIT:
class << self doesn't accept a block; class_eval does, hence it preserves the state of local variables. I've modified my solution to use it.

Unable to access functions in modules via scope operator

I am working on a program in Ruby with Watir that has a series of modules.
An example module would look like:
demoMod1.rb
DEMOMOD1 = true #this module has been loaded
module demoMod1
def reload(browser)
browser.goto("demo1.html")
end
end
There are several files with very similarly named functions (example: reload() ), so demoMod2 and demoMod3 both have the same reload function (except pointing to different pages).
The modules are commented out of the require list when that area is not expected to be tested. (as per design specs)
The program then dynamically loads test case files (code to do that is below in main.rb). Each test case file contains a runTest() function, which calls the appropriate sections from the modules. Each test checks if the module is loaded, and if it has, the test executes.
A sample test case file would look like:
test1.rb
def runTest(browser) # test case 1
if(defined?(DEMOMOD1)).nil?
return nil #if the module is not there, dont run this test
end
demoMod1::reload(browser)
end
As per specs, a single file lists all of the modules to be required, so that a user can just comment out the sections to not be tested without having to look at the main file. The main file is below.
main.rb
require 'Watir'
SITE = #internal site - address removed =/ sorry
FOLDER = File.dirname(__FILE__) + '/lib'
SHORTNAME = 'Test' #for reporting
LONGNAME = 'In Development' #for reporting
#I know - globals.. yuck... avoid...
require FOLDER + '/lib/requires'
#includes the function to require each module from the module folder
require FOLDER + '/www/include/report.module'
#creates an html report to view the test results
# Get list of tests
def getTests()
Dir.chdir(FOLDER + '/Tests')
files = Dir.glob("*.rb")
return files
end #eofunc
# Run each test
def runTests(files, browser, report)
testSuite = String.new
files.each do |test|
suite = test.split(" - ") # name convention: suite - testname.rb
if !(suite[0]==testSuite)
testSuite = suite[0]
report.newTestSuiteName(testSuite)
end #eoif
load FOLDER + '/Tests/' + test #load the test case file
runTest(browser) #run the test case
end #eoeach
end #eofunc
begin
browser = Watir::Browser.new
browser.goto(SITE)
report = Report.new()
reportname = report.createReport(SHORTNAME, LONGNAME) #vars in Vars.rb
files = getTests()
runTests(files, browser, report)
report.finishReport(reportname, SITE)
browser.close
report.openReport(reportname) #launch report in a new browser
end #eofunc
I would expect in each of the runTest() to be able to access the demoMod1::reload() and demoMod2::reload(), but this is not the case. I get an error for undefined method 'reload' for demoMod1:Module. I can resolve the error by using include demoMod1 but then (if I have this correctly), the scope is not needed, and using just reload() would work. However this would require each module have entirely unique names, and that is not a great solution.
What am I missing?
Have you tried using a Test/Unit / rSpec or something similar to run your tests?
Beyond that, my understanding is:
Requiring the file the module is in only makes it available for inclusion. You need to use include to put it into a class in order to use it.
Have you considered making a class for your tests, e.g. Test and then TestType1 and TestType2 inheriting from that parent class?
For example:
require fileWithYourModules
class Test
# stuff I want to have available to all tests
end
class TestSubType1 < Test
include demoMod1
end
class TestSubType2 < Test
include demoMod2
end
Which then allows you to call the same method name and have it behave differently depending on the test class it has.
I'm sure there are more elegant solutions, I'm just a tester and not a programmer! :)
By changing the function names in the modules I can get it to work.
example module:
DEMOMOD1 = true
module DemoMod1
def DemoMod1.reload(browser) #<---------
browser.goto('demo1.htm')
end
end
Line with the <--- arrow is the main change (also had to change the module name, did not have a capital first letter...oops)
example test case:
def runTest(browser) # test case 1
if(defined?(DEMOMOD1)).nil?
return nil #if the module is not there, dont run this test
end
DemoMod1::reload(browser)
### OR ### Both work
DemoMod1.reload(browser)
end

Calling a module inside another module

I am calling a method in a module from another module and am getting a weird error.
require 'nmap'
...
module Enumeration::Hostnames
def reverse_dns ip_addrs
...
ip_addrs.each do |ip_addr|
list = ListScan.test ip_addr #this is the problem
...
end
...
ListScan is in the nmap file.
module ListScan
def ListScan.test target
target = '-sL ' + target
ListScan::parse_results Nmap::Parser.parsescan('nmap',target)
end
...
end
The error is `const_missing': uninitialized constant Enumeration::Hostnames::ListScan (NameError) on the line ListScan.test ip_addr.
Why is it assuming that ListScan is in the Enumeration::Hostnames module? Mixing in ListScan to Hostnames didn't work.
Ruby searches for constants starting from the current context, which in this case is Enumeration::Hostnames.
Try using
::ListScan.test ip_address

Resources