How to access members of another class in a different file - ruby

I'm using ruby-prolog. I want to run a task to query a fact.
demo.rb:
require 'ruby-prolog'
c = RubyProlog::Core.new
c.instance_eval do
person['name','brian'].fact
person['name','James'].fact
puts 'all the names are: '
p query(person['name', :A])
end
This works great. Now I want to run the query inside of Rake. That is a problem because I don't know how to access person[] from the other file.
Rakefile.rb:
require_relative 'demo.rb'
task :test do |variable|
puts 'all the names are: '
p query(person['name', :A])
end
Error:
all the names are: rake aborted! NameError: undefined local variable
or method `person' for main:Object
I'm hoping this can be solved by passing an object back somehow. I tried accessing c, but it did not work out. Any ideas?

In your demo file, both variables person and c are local variables and will not be accessible from outside of that context. If you require demo.rb into an irb session, the behavior should be the same; neither c nor person will be defined.
A good way of dealing with this in rake tasks is to keep any and all logic out of the rake task itself, and only call out to another object that takes care of the task. For a quick and dirty example, you could alter your code as such:
# demo.rb
require 'ruby-prolog'
class Demo
def self.run_demo
# Existing code:
c = RubyProlog::Core.new
c.instance_eval do
person['name','brian'].fact
person['name','James'].fact
puts 'all the names are: '
p query(person['name', :A])
end
end
end
and
# Rakefile.rb
require_relative 'demo.rb'
task :test do
Demo.run_demo
end

Related

How to use login credentials from yaml in ruby

I'm new in ruby and I can't move forward from using login cred. from a yml file for a ruby project .I have a basic yml file
login:
urls:
gmail: "https://accounts.google.com/signin"
users:
username: something
password: something_new
I've created a yml.rb with require yml ,and access the yml path & loading the file .
But I don't know how to go through users/username ... in my test.rb :( .I've added in the "it " a variable to store the yml class and at the end i'm trying with
expect data['valid_user']
expect data['login']['urls']['gmail']
expect data['login']['users']['username']
but in the terminal I receive th error "NoMethodError: undefined method `[]' for nil:NilClass "
Update
Here is my yml.rb
require 'rspec'
require 'yaml'
class YamlHelper
#env = {}
def initialize
file = "#{Dir.pwd}path of yml file"
#env = YAML.load_file(file)
end
def get_variables
#env
end
end
Here is my test.rb file
describe 'My behaviour' do
before(:each) do
#browser = Selenium::WebDriver.for :firefox
end
it 'verifies yml login' do
yaml_helper = YamlHelper.new
data = yaml_helper.get_variables
expect data['valid_user']
expect test_data['login']['urls']['gmail']
expect test_data['login']['users']['username']
expect test_data['login']['users']['password']
end
after(:each) do #browser.quit
end
Can anyone take a look ?
thanks in advance
Have a lovely day
It looks like the code is almost there. When I'm debugging this sort of thing I'll often try to distil it down to the most basic test first
Something like:
require 'yaml'
file = "#{Dir.pwd}/data.yml"
data = YAML.load_file(file)
data['valid_user']
#=> nil
data['login']['urls']['gmail']
#=> "https://accounts.google.com/signin"
data['login']['users']['username']
#=> "something"
From the above you can see there's probably a typo in your test.rb file: test_data should most likely be data. Also, your YAML file doesn't contain the valid_user key, so you probably want to remove it from the test, at least for now.
The other two keys load fine.
The error you're seeing NoMethodError: undefined method '[]' for nil:NilClass means that one of the hashes you're variables you're treating like a hash is actually nil. This sort of bug is fairly common when you're diving into nested hashes. It means one of two things:
You've correctly descended into the hash, but the data is not present in the YAML.
The data is present in the YAML, but you're not getting to it correctly.
One change you can make that will make this code a bit more resilient is to replace:
test_data['login']['users']['username']
with:
test_data.dig('login', 'users', 'username')
The latter uses dig, which delves into the data structure and tries to return the value you're after, but if it gets a nil back at any point it'll just return nil, rather than throwing an exception.
Finally, for the test you've pasted here, you don't need the before(:each) or after(:each) blocks – Selenium is only necessary for browser testing.

Modify Rake Task at test/runtime

Consider the following
class Foo
def bar
1
end
end
And then two rake tasks ...
task :something
InvokeSomething(Foo.new)
end
task :test
do_some_testing
end
def do_some_testing
setup_test
`rake something`
check_if_it_did_everything_correctly
end
After executing setup_test, I want to modify the source for class Foo to return 2 for method bar.
Ideas:
Create a rake task that gets invoked before I define the rake task something and I modify the source code directly. So that something is defined with a different source code.
Somehow modify the Rake task so that that particular method is overridden. At the very least I need a reference to the rake task during runtime.
There are multiple ways to do this.
You can get the reference from Rake::Task["something"] during do_some_testing and re-define a method there.
But there is another even simpler way - which worked for me. Set the value in bar as an environment variable. And modify the environment variable when needed.

Ruby TDD with Rspec (Basic Questions)

I am trying to run a very basic test with Terminal and Sublime Text 3. My simple test runs, but fails (undefined local variable or method 'x')
My folder hierarchy looks like this:
spec_helper.rb looks like this:
require_relative '../test'
require 'yaml'
test_spec.rb is extremely basic
require 'spec_helper.rb'
describe "testing ruby play" do
it "finds if x is equal to 5" do
x.should eql 5
end
end
and my test.rb file has x = 5 That's it.
Will a variable only be recognizable if it's part of a class? And do I need to call a new class every time I run my test?
From the docs
require(name) → true or false
Loads the given name, returning true if successful and false if the feature is already
loaded.
[snip]
Any constants or globals within the loaded source file will be
available in the calling program’s global namespace. However, local
variables will not be propagated to the loading environment.
You could use a constant in your required file:
X = 5
...
X.should eql 5 # => passes
But you probably want to do something entirely different here. Perhaps you could expand on the question and explain what you are trying to accomplish.

Load a Ruby TestCase Without Running It

I'm trying to write a custom tool that runs ruby unit tests with my customizations.
What I need it to do is to load a certain TestCase from given file(through require or whatever), and then run it after doing some calculations and initializations.
Problem is, the moment I require "test/unit" and a test case, it runs immediately.
What can I do with this?
Thanks.
Since you're running 1.9 and test/unit in 1.9 is merely a wrapper for MiniTest, the following approach should work:
implement your own custom Runner
set MiniTest's runner to your custom runner
Something like (shameless plug from EndOfLine Custom Test Runner, adjusted to Ruby 1.9):
fastfailrunner.rb:
require 'test/unit'
class FastFailRunner19 < MiniTest::Unit
def _run args = []
puts "fast fail runner"
end
end
~
example_test.rb:
require 'test/unit'
class ExampleTest < Test::Unit::TestCase
def test_assert_equal
assert_equal 1, 1
end
def test_lies
assert false
end
def test_exceptions
raise Exception, 'Beware the Jubjub bird, and shun the frumious Bandersnatch!'
end
def test_truth
assert true
end
end
run.rb:
require_relative 'fast_fail_runner'
require_relative 'example_test'
MiniTest::Unit.runner= FastFailRunner19.new
If you run this with
ruby run.rb
the custom FastFailRunner19 will be used, which does nothing.
What about reading file content as a regular text file and doing eval on its content after you initialize/calculate things you say? It may not be sufficient for your needs and may require manual setup and execution of testing framework.
Like that (I put heredoc instead of reading file). Basically content is just a string containing your test case code.
content = <<TEST_CASE
class YourTestCase
def hello
puts 'Hello from eval'
end
end
YourTestCase.new.hello
TEST_CASE
eval content
Note: Altough I'd rather not use eval if there is another way. One should be extra careful when evaling code from string manually in any language.
You could collect the test cases you want to deferred its executions and store them in an array. Afterwards you would create a block execution code. For instance:
test_files = ['test/unit/first_test.rb'] #=> Testcases you want to run
test_block = Proc.new {spec_files.each {|f|load f} } #=> block storing the actual execution of those tests.
Once you're ready to call those testcases you just do test_block.call.
To generalize a bit, when thinking about deferring or delaying code executions, closures are a very elegant and flexible alternative.

How to really reset a unit test?

I'd like to test class and gem loading. Have a look at the following stupid test case:
require 'rubygems'
require 'shoulda'
class SimpleTest < Test::Unit::TestCase
context 'This test' do
should 'first load something' do
require 'bundler'
assert Object.const_defined? :Bundler
end
should 'second have it reset again' do
assert !Object.const_defined?(:Bundler)
end
teardown do
# This works, but is tedious and unclean
#Object.send :remove_const, :Bundler rescue nil
# Instead I want something like this ;)
magic_reset
end
end
end
How about creating a subclass of Test::Unit::TestCase which runs the test method in a forked process?
class ForkingTestCase < Test::Unit::TestCase
def run(...)
fork do
super.run(...)
# somehow communicate the result back to the parent process
# this is the hard part
end
end
end
If this can be implemented, it should then just be a matter of changing the base class of your test case.
AFAIK, you cannot unload a file that you have loaded. You need to start a separate Ruby process for every test. (Or a separate Ruby instance if you are running on a Ruby implementation which supports multiple instances in the same process.)
Try using Kernel#load with wrap set to true:
load(filename, wrap=false) → true
Loads and executes the Ruby program in the file filename. If the
filename does not resolve to an absolute path, the file is searched
for in the library directories listed in $:. If the optional wrap
parameter is true, the loaded script will be executed under an
anonymous module, protecting the calling program’s global namespace.
In no circumstance will any local variables in the loaded file be
propagated to the loading environment.
each time you want to do a test of bundler, load it into a new anonymous module, do your tests on the bundler class within that module, and then go onto your next test.
If your code refers to the Bundler constant, then you'd have to set and unset that constant, though.
I haven't tried it myself, but can't see why it wouldn't work.
Try keeping track of what constants were defined before your tests started, and what constants are defined after your test finished, and remove the constants that were defined during the test.
I guess this isn't so much telling you how to call magic_reset as how to implement magic_reset.

Resources