Ruby load does not see static methods in the class - ruby

I am experimenting with ruby (2.4.1p111) and with the load method and it does have some strange behavior:
I have two files:
mytest.rb:
class MyClass
def self.greet(param)
puts "Got called: #{param}"
end
greet 'Called locally'
load "./testld.rb"
end
and the loaded file:
testld.rb:
greet 'Called by load'
I understood from documentation that the loaded code from testld.rb and the method call right in the MyClass should behave the same. Instead, I am getting:
-bash-4.2$ ruby mytest.rb
Got called: Called locally
/Blacksmith/RB/testld.rb:1:in `<top (required)>': undefined method `greet'
for main:Object (NoMethodError)
from mytest.rb:9:in `load'
from mytest.rb:9:in `<class:MyClass>'
from mytest.rb:1:in `<main>'
Any idea what I do wrong?

You need to call MyClass.greet in testld.rb, that is because greet is a class method defined in the class MyClass.
Here is documentation for class methods.

Wherever you load a file, the loaded file is always evaluated within the main environment. The value of self in the main environment of testld.rb is the main environment of the entire script. Hence, your
greet 'Called by load'
is not equivalent to
MyClass.greet 'Called by load'
as you expected.

Related

Why do I get undefined method `mktmpdir' for Dir:Class in irb using Ruby 2.6.3?

https://ruby-doc.org/stdlib-2.6.3/libdoc/tmpdir/rdoc/Dir.html contains a description about the mktmpdir method of the Dir class.
mktmpdir(prefix_suffix=nil, *rest)
::mktmpdir creates a temporary directory.
An app uses it with no error but why it is not possible to run it in console / irb?
I have tested with Dir.new ... as well, below my attempt with Dir.mktmpdir.
irb(main):010:0> Dir.mktmpdir {|dir| dir is ".../d..." }
Traceback (most recent call last):
5: from C:/Ruby26/bin/irb.cmd:31:in `<main>'
4: from C:/Ruby26/bin/irb.cmd:31:in `load'
3: from C:/Ruby26/lib/ruby/gems/2.6.0/gems/irb-1.0.0/exe/irb:11:in `<top (required)>'
2: from (irb):10
1: from (irb):10:in `rescue in irb_binding'
NoMethodError (undefined method `mktmpdir' for Dir:Class)
Dir.methods is not showing this method but only mkdir.
What is missing respectively what is the reason for this behavior?
You're confusing the patches to the Dir class that tmpdir in the standard library adds with the plain unpatched Dir class in core. If you look at the tmpdir package's documentation:
https://ruby-doc.com/stdlib/libdoc/tmpdir/rdoc/Dir.html
then you'll find Dir.mktmpdir (note the stdlib/libdoc/tmpdir) in the path. But if we look at Dir in core:
https://ruby-doc.org/core/Dir.html
you'll find all the usual class methods (chdir, chroot, ...) but no mktmpdir.
If you want to use mktmpdir then you have to require 'tmpdir':
Dir.method(:mktmpdir)
# NameError (undefined method `mktmpdir' for class `#<Class:Dir>')
require 'tmpdir'
Dir.method(:mktmpdir)
# #<Method: Dir.mktmpdir>

How can I call Rake methods from a module

I have a lot of utility functions in my rake files, some of which create rake tasks. I want to move these utility functions into a module to avoid name clashes, but when I do the rake methods are no longer available.
require 'rake'
directory 'exampledir1'
module RakeUtilityFunctions
module_function
def createdirtask dirname
directory dirname
end
end
['test1', 'test2', 'test3'].each { |dirname|
RakeUtilityFunctions::createdirtask dirname
}
The error I get is:
$ rake
rake aborted!
undefined method `directory' for RakeUtilityFunctions:Module
C:/dev/rakefile.rb:8:in `createdirtask'
C:/dev/rakefile.rb:13:in `block in <top (required)>'
C:/dev/rakefile.rb:12:in `each'
C:/dev/rakefile.rb:12:in `<top (required)>'
As far as I can tell the directory method is placed on the ruby top-level by the following code in Rake:
# Extend the main object with the DSL commands. This allows top-level
# calls to task, etc. to work from a Rakefile without polluting the
# object inheritance tree.
self.extend Rake::DSL
Is there a simple way of call functions that have been placed on the top-level like this?
When you define a Module, the code within that module has a new scope.
So directory within RakeUtilityFunctions is in a different scope to the top-level code.
As you haven't defined directory within RakeUtilityFunctions you get an undefined method error.
Have a look at the Scope Gate section of this article.
I have figured it out now. With help from #ReggieB, I discovered this question: ways to define a global method in ruby.
It contained an excerpt from the rake change log.
If you need to call 'task :xzy' inside your class, include Rake::DSL into the class.
So, the easiest way to do this is to extend the module with Rake::DSL:
require 'rake'
directory 'exampledir1'
module RakeUtilityFunctions
self.extend Rake::DSL ### This line fixes the problem!
module_function
def createdirtask dirname
directory dirname
end
end
['test1', 'test2', 'test3'].each { |dirname|
RakeUtilityFunctions.createdirtask dirname
}

rspec Cannot load such file after working the first time

When running through the rspec course in codeschool I keep running into the same problem. I will set up as requested and after creating zombie_spec.rb and running rspec I get the proper output listed below:
Justins-MacBook-Pro:rubyproject Justin$ rspec spec/lib/zombie_spec.rb
Run options: include {:focus=>true}
All examples were filtered out; ignoring {:focus=>true}
*
Pending:
A Zombie is named Ash
# Not yet implemented
# ./spec/lib/zombie_spec.rb:3
Finished in 0.00929 seconds
1 example, 0 failures, 1 pending
Randomized with seed 7259
As I continue on with the first video and create the class Zombie as mentioned I receive this error when running rspec again:
Justins-MacBook-Pro:rubyproject Justin$ rspec spec/lib/zombie_spec.rb
/usr/local/rvm/rubies/ruby-1.9.3-p392/lib/ruby/site_ruby/1.9.1/rubygems/custom_require.rb:36:in `require': cannot load such file -- zombie (LoadError)
from /usr/local/rvm/rubies/ruby-1.9.3-p392/lib/ruby/site_ruby/1.9.1/rubygems/custom_require.rb:36:in `require'
from /Users/Justin/rubyproject/spec/lib/zombie_spec.rb:2:in `<top (required)>'
from /usr/local/rvm/gems/ruby-1.9.3-p392/gems/rspec-core-2.13.1/lib/rspec/core/configuration.rb:819:in `load'
from /usr/local/rvm/gems/ruby-1.9.3-p392/gems/rspec-core-2.13.1/lib/rspec/core/configuration.rb:819:in `block in load_spec_files'
from /usr/local/rvm/gems/ruby-1.9.3-p392/gems/rspec-core-2.13.1/lib/rspec/core/configuration.rb:819:in `each'
from /usr/local/rvm/gems/ruby-1.9.3-p392/gems/rspec-core-2.13.1/lib/rspec/core/configuration.rb:819:in `load_spec_files'
from /usr/local/rvm/gems/ruby-1.9.3-p392/gems/rspec-core-2.13.1/lib/rspec/core/command_line.rb:22:in `run'
from /usr/local/rvm/gems/ruby-1.9.3-p392/gems/rspec-core-2.13.1/lib/rspec/core/runner.rb:80:in `run'
from /usr/local/rvm/gems/ruby-1.9.3-p392/gems/rspec-core-2.13.1/lib/rspec/core/runner.rb:17:in `block in autorun'
After this I removed everything and uninstalled and reinstalled rspec. Retried it again and returned the same results.
Any clue what is going on?
Thank you in advance for your help!
require_relative will load the file from the same directory as the rspec file
I had pretty much the same issue, though mine never worked even once at first. Updating the zombie_spec.rb file to show the full path of my zombie.rb file seemed to get it working properly.
Eg:require "/home/me/ruby/spec/lib/zombie"
I use this and it worked
require_relative "zombie.rb"
Well if this still a problem I got into the same issue so I created a folder under spec/lib where I put all the codes and another spec/test where goes all the tests then it worked.
and I also added require_relative here is the code snippet
zombie_spec.rb
require 'spec_helper'
require_relative '../lib/zombie'
describe Zombie do
it "has a name called'Jack'" do
zb = Zombie.new
zb.name.should == "Jack"
end
it "has no brains" do
zb = Zombie.new
zb.should be_intelligent == false
end
end
zombie.rb
class Zombie
attr_accessor :name
def initialize
#name = "Jack"
end
def intelligent?
false
end
end

Undefined method error when trying to run around hooks

I am trying to run given scenarios as many times per each account type. I found out in other thread that "around hooks" could be the best option to do it the DRY way.
Below is my code:
require 'watir-webdriver'
require 'page-object/page_factory'
require 'page_navigation'
require 'data_magic'
require_relative 'pages/login_page'
Before do
beforeCodeBlock
...
end
Around('#hooks_test') do|scenario, block|
DataMagic.load('default.yml')
account_type = {"listener" => DataMagic.yml["listener_data"],
"free" => DataMagic.yml["free_data"],
"premium" => DataMagic.yml["premium_data"]}
account_type .each {|key, value|
puts "Running scenario: #{scenario.name} as #{key} user"
visit_page(LoginPage)
on_page(LoginPage).sign_in(value["username"],value["password"]) #Login as
block.call
}
end
After do
afterCodeBlock
...
end
So when I run the feature file I get the following output and subsequent error:
Running scenario: Hooks test as listener user
undefined method `visit_page' for nil:NilClass (NoMethodError)
/myProject/features/support/hooks.rb:38:in `block (2 levels) in <top (required)>'
/myProject/features/support/hooks.rb:35:in `each'
/myProject/features/support/hooks.rb:35:in `Around'
Everything seems to be working as expected until the execution deals with the visit_page method that belongs to PageObject gem.
There are possibly two problems I see here.
First of all you are getting the error because you did not register the PageObject::PageFactory module with World. Add this line after the requires:
World(PageObject::PageFactory)
Second, you are using an Around block and calling the same Scenario multiple times. From what I am seeing, you are doing this so that it will exercise the same behavior with different logged in users. If the behavior is exactly the same, what are you accomplishing by running it three times?

Ruby NoMethodError in Redmine from script/runner

I have some Ruby code I need to execute in the Redmine Ruby app in order to enable a module in all projects at once.
Surprisingly, ruby does access the Projects, but raises a NoMethodError when accessing a few particular methods inside each of the "Project" objects.
Here is the code:
Project.find(:all).each do |project|
print "Enabling modules for project '#{project.identifier}' ... "
puts project.methods.sort # this does print "enabled_module_names"
puts project.enabled_module_names
end
This fails with:
hostname:/srv/apps/redmine# script/runner vendor/plugins/customplugin/lib/enable_modules.rb
/var/lib/gems/1.8/gems/activerecord-2.3.5/lib/active_record/attribute_methods.rb:260:in `method_missing': undefined method `enabled_module_names' for #<Project:0x7f28985c1cb0> (NoMethodError)
from vendor/plugins/customplugin/lib/enable_modules.rb:14
from vendor/plugins/customplugin/lib/enable_modules.rb:7:in `each'
from vendor/plugins/customplugin/lib/enable_modules.rb:7
from /usr/lib/ruby/1.8/rubygems/custom_require.rb:31:in `eval'
from /var/lib/gems/1.8/gems/rails-2.3.5/lib/commands/runner.rb:46
from /usr/lib/ruby/1.8/rubygems/custom_require.rb:31:in `gem_original_require'
from /usr/lib/ruby/1.8/rubygems/custom_require.rb:31:in `require'
from script/runner:3
I have scratched my head a lot, but I can't grasp why would the code find the "Project" symbol but not the methods within, especially as "project.methods" does indeed list "enabled_module_names".
Any help most welcome.
Are you sure that enable_module_names is an instance method?
Is it within Project.instance_methods?
Edit (summary of the comments below):
In earlier versions, you have to use following:
enabled_module_names = project.enabled_modules.collect(&:name)
The getter is present in later versions only (see rev.4460 for details of this change)

Resources