Why is Ruby seeing `A.run` in class B as a constant and not a class? - ruby

so this is my first question on stack overflow and I am new to Ruby, so if this is a simple question, please be nice.
I am starting off in OOP and making a game. What I think is wrong is that Ruby is thinking that a different class is a constant in the current class.
Here is my code:
./a.rb
require "./b"
class A
class << self
def run
puts "A ran."
end
end
end
./b.rb
class B
require './a'
def test
A.run
end
end
b = B.new
b.test
When I run ruby b.rb, I get:
/Users/alexstriff/Dropbox/Code/ruby/ex45/b.rb:5:in `test': uninitialized constant B::A (NameError)
from /Users/alexstriff/Dropbox/Code/ruby/ex45/b.rb:11:in `<top (required)>'
from /System/Library/Frameworks/Ruby.framework/Versions/2.0/usr/lib/ruby/2.0.0/rubygems/core_ext/kernel_require.rb:45:in `require'
from /System/Library/Frameworks/Ruby.framework/Versions/2.0/usr/lib/ruby/2.0.0/rubygems/core_ext/kernel_require.rb:45:in `require'
from /Users/alexstriff/Dropbox/Code/ruby/ex45/a.rb:1:in `<top (required)>'
from /System/Library/Frameworks/Ruby.framework/Versions/2.0/usr/lib/ruby/2.0.0/rubygems/core_ext/kernel_require.rb:45:in `require'
from /System/Library/Frameworks/Ruby.framework/Versions/2.0/usr/lib/ruby/2.0.0/rubygems/core_ext/kernel_require.rb:45:in `require'
from b.rb:2:in `<class:B>'
from b.rb:1:in `<main>'
So why is A.run seen as a constant by class B, instead of as a class?
Edit:
Could it possibly be a problem with the 'circular require'? I ran this under ruby -w and that came up.

Yes, as you mentioned in your edit, I believe your issue is with a circular require. As you can see, when you run ruby b.rb, you require a.rb, which then requires b.rb again, etc. Instead, I would advise removing the require in a.rb, as it does not seem to depend on b.rb.
As a separate issue, I think the error you are getting is because you don't specify that A is a top-level class. Also, having the require inside of class B does not make A accessible inside B, so I would move it outside instead:
require './a'
class B
def test
::A.run
end
end
b = B.new
b.test

Related

Circular require issue with Ruby

I am trying to implement a currency based application using Ruby, I find that:
require 'dollar'
require 'franc'
puts 'Why does this not work?'
class Money
attr_reader :amount
def self.dollar(number)
Dollar.new number
end
def ==(other)
self.amount == other.amount
end
def self.franc(number)
Franc.new(number)
end
end
I have the Franc class that looks like this:
require 'money'
class Franc < Money
attr_reader :amount
def initialize(amount)
#amount = number
end
def times(mul)
amount = #amount * mul
Franc.new(amount)
end
def ==(other)
return false unless other.is_a? self.class
super
end
end
This is a direct translation of some of the code from the Kent Beck book to Ruby. When I run bin/rspec I see:
/home/vamsi/Do/wycash/lib/franc.rb:3:in `<top (required)>': uninitialized constant Money (NameError)
from /home/vamsi/Do/wycash/lib/money.rb:2:in `require'
from /home/vamsi/Do/wycash/lib/money.rb:2:in `<top (required)>'
from /home/vamsi/Do/wycash/lib/dollar.rb:1:in `require'
from /home/vamsi/Do/wycash/lib/dollar.rb:1:in `<top (required)>'
from /home/vamsi/Do/wycash/spec/dollar_spec.rb:1:in `require'
from /home/vamsi/Do/wycash/spec/dollar_spec.rb:1:in `<top (required)>'
from /home/vamsi/Do/wycash/.bundle/ruby/2.3.0/gems/rspec-core-3.5.4/lib/rspec/core/configuration.rb:1435:in `load'
from /home/vamsi/Do/wycash/.bundle/ruby/2.3.0/gems/rspec-core-3.5.4/lib/rspec/core/configuration.rb:1435:in `block in load_spec_files'
from /home/vamsi/Do/wycash/.bundle/ruby/2.3.0/gems/rspec-core-3.5.4/lib/rspec/core/configuration.rb:1433:in `each'
from /home/vamsi/Do/wycash/.bundle/ruby/2.3.0/gems/rspec-core-3.5.4/lib/rspec/core/configuration.rb:1433:in `load_spec_files'
from /home/vamsi/Do/wycash/.bundle/ruby/2.3.0/gems/rspec-core-3.5.4/lib/rspec/core/runner.rb:100:in `setup'
from /home/vamsi/Do/wycash/.bundle/ruby/2.3.0/gems/rspec-core-3.5.4/lib/rspec/core/runner.rb:86:in `run'
from /home/vamsi/Do/wycash/.bundle/ruby/2.3.0/gems/rspec-core-3.5.4/lib/rspec/core/runner.rb:71:in `run'
from /home/vamsi/Do/wycash/.bundle/ruby/2.3.0/gems/rspec-core-3.5.4/lib/rspec/core/runner.rb:45:in `invoke'
from /home/vamsi/Do/wycash/.bundle/ruby/2.3.0/gems/rspec-core-3.5.4/exe/rspec:4:in `<top (required)>'
from bin/rspec:17:in `load'
from bin/rspec:17:in `<main>'
I was going to add this as a comment when I realised it would start getting too lengthy.
When you say you have translated directly from Kent Beck's book I assume you are referring to his TDD by Example book (it's the only book I can find of his that references the currency example). I can't actually find an example in that book however that refers to cyclic dependancy, however from previous experience with Java and C++ you normally try to break up a cyclic dependancy by implementing an interface - for Ruby, you might want to refer to the following SO question which has good answers to this problem:
Circular Dependancies in Ruby
Having said that I think your design is broken. Your Money class should be defining the generic behaviour and attributes for all Money types. It should know nothing about Franc's or Dollars, etc... Specific behaviour for Franc should be encapsulated entirely within the Franc class. Either use a single class to handle all the currencies or use inheritance - it doesn't make much sense to do both.
You should put require 'franc' after you defined Money in your first script.
The class Money will be then defined when Ruby executes your second_script.
EDIT:
Circular require isn't an issue :
# a.rb
puts "A"
require_relative 'b'
# b.rb
puts "B"
require_relative 'a'
# ruby a.rb
A
B
A
Replacing require_relative with load './b.rb' would result in an infinite loop and "stack level too deep" Error.
Still, you should probably define Money in money.rb, Franc in franc.rb, ... and not put anything about Franc in Money.

Celluloid 0.17.3 giving unexpected "undefined method" error

I have started using Celluloid gem this morning for that first time. I am following this Railscasts tutorial and trying to figure things out.
I have a class called "SomeClass" and it has only one method. Here is the code:
require 'celluloid'
class SomeClass
include Celluloid
def initialize(name)
#name = name
end
def assholify()
puts "#{#name} has become an ASSHOLE."
end
end
When I create new instances of the class and call its method (with a bang i.e. "assholify!"), I am getting the undefined method 'assholify!', error. But Celluloid is supposed to trigger the method asynchronously when it is called with a bang. So here is how I am calling the method:
names = ['John', 'Tom', 'Harry']
names.each do |name|
n = SomeClass.new name
n.assholify!
end
Here is the full backtrace of the error:
I, [2016-09-09T11:28:02.488618 #3682] INFO -- : Celluloid 0.17.3 is running in BACKPORTED mode. [ http://git.io/vJf3J ]
/home/railsdev/.rvm/gems/ruby-2.3.1/gems/celluloid-0.17.3/lib/celluloid/calls.rb:42:in `rescue in check': undefined method `assholify!' for #<SomeClass:0x10897dc> (NoMethodError)
from /home/railsdev/.rvm/gems/ruby-2.3.1/gems/celluloid-0.17.3/lib/celluloid/calls.rb:39:in `check'
from /home/railsdev/.rvm/gems/ruby-2.3.1/gems/celluloid-0.17.3/lib/celluloid/calls.rb:26:in `dispatch'
from /home/railsdev/.rvm/gems/ruby-2.3.1/gems/celluloid-0.17.3/lib/celluloid/call/sync.rb:16:in `dispatch'
from /home/railsdev/.rvm/gems/ruby-2.3.1/gems/celluloid-0.17.3/lib/celluloid/cell.rb:50:in `block in dispatch'
from /home/railsdev/.rvm/gems/ruby-2.3.1/gems/celluloid-0.17.3/lib/celluloid/cell.rb:76:in `block in task'
from /home/railsdev/.rvm/gems/ruby-2.3.1/gems/celluloid-0.17.3/lib/celluloid/actor.rb:339:in `block in task'
from /home/railsdev/.rvm/gems/ruby-2.3.1/gems/celluloid-0.17.3/lib/celluloid/task.rb:44:in `block in initialize'
from /home/railsdev/.rvm/gems/ruby-2.3.1/gems/celluloid-0.17.3/lib/celluloid/task/fibered.rb:14:in `block in create'
from (celluloid):0:in `remote procedure call'
from /home/railsdev/.rvm/gems/ruby-2.3.1/gems/celluloid-0.17.3/lib/celluloid/call/sync.rb:45:in `value'
from /home/railsdev/.rvm/gems/ruby-2.3.1/gems/celluloid-0.17.3/lib/celluloid/proxy/sync.rb:22:in `method_missing'
from some_class.rb:18:in `block in <main>'
from some_class.rb:16:in `each'
from some_class.rb:16:in `<main>'
Why am I getting this error? Is it the right way to call the function? Also how do I get rid of Celluloid 0.17.3 is running in BACKPORTED mode. warning?
The undefined method error occurred because actor methods are not called with a bang in the recent versions of celluloid gem. Instead you call the method like this: n.async.assholify. So here is what the code should look like:
names = ['John', 'Tom', 'Harry']
names.each do |name|
n = SomeClass.new name
n.async.assholify # Instead of "n.assholify!"
end
For "Celluloid 0.17.0 is running in BACKPORTED mode" warning, take a look at this wiki. Backported Mode is the default, for a limited time. If you use require 'celluloid/current' instead of require 'celluloid', you should not see this warning.

NameError: uninitialized constant error

Given the following code:
module Backup
module Destination
class Base
def initialize
puts 'Base'
end
end
end
end
module Backup
module Destination
class Test < Base
def initialize
puts 'Test'
super
end
end
end
end
Backup::Destination::Test.new
This works as expected, outputting:
Test
Base
However if I split things up like this:
# lib/backup.rb
require_relative 'backup/destination/base'
module Backup; end
# lib/backup/destination/base.rb
require_relative 'test'
module Backup
module Destination
class Base
def initialize
puts 'Base'
end
end
end
end
# lib/backup/destination/test.rb
module Backup
module Destination
class Test < Base
def initialize
puts 'Test'
super
end
end
end
end
And execute with the following (from irb):
require_relative 'lib/backup'
I get this error:
NameError: uninitialized constant Backup::Destination::Base
from /lib/backup/destination/test.rb:3:in `<module:Destination>'
from /lib/backup/destination/test.rb:2:in `<module:Backup>'
from /lib/backup/destination/test.rb:1:in `<top (required)>'
from /lib/backup/destination/base.rb:1:in `require_relative'
from /lib/backup/destination/base.rb:1:in `<top (required)>'
from /lib/backup.rb:1:in `require_relative'
from /lib/backup.rb:1:in `<top (required)>'
from (irb):1:in `require_relative'
What am I missing?
Note: I couldn't post the above without adding more details. Stupid feature because in this case code is worth a thousand words. (this text allowed the question to be posted)
The problem is that you are requiring test.rb before your Base class is defined. One possible solution is to move your require to the bottom of base.rb.
Another possible solution is to remove your require from base and require both files in the correct order from backup.
Made the following changes to fix the problem:
# lib/backup.rb
require_relative 'backup/destination/base'
require_relative 'backup/destination/test'
module Backup; end
And removed the require_relative statement from lib/backup/destination/base.rb. This fixed the order of the require_relative statements. I mistakenly thought the files were required before anything was executed.

How to define Ruby Test::Unit testcase with `must`

I had a test case of the following form:
require 'test/unit'
class SomeTests < Test::Unit::TestCase
def test_should_do_action
assert true
end
end
and have re-written it using must as suggested in the book A Test::Unit Trick to Know About:
require 'test/unit'
class SomeTests < Test::Unit::TestCase
must "do action" do
assert true
end
end
And when I run it, I get an undefined method 'must' error shown as follows:
SomeTests.rb:3:in `<class:SomeTests>': undefined method `must' for SomeTests:Class (NoMethodError) from
SomeTests.rb:2:in `<top (required)>' from
/System/Library/Frameworks/Ruby.framework/Versions/2.0/usr/lib/ruby/2.0.0/rubygems/core_ext/kernel_require.rb:55:in `require' from
/System/Library/Frameworks/Ruby.framework/Versions/2.0/usr/lib/ruby/2.0.0/rubygems/core_ext/kernel_require.rb:55:in `require' from
/System/Library/Frameworks/Ruby.framework/Versions/2.0/usr/lib/ruby/2.0.0/rake/rake_test_loader.rb:10:in `block (2 levels) in <main>' from
/System/Library/Frameworks/Ruby.framework/Versions/2.0/usr/lib/ruby/2.0.0/rake/rake_test_loader.rb:9:in `each' from
/System/Library/Frameworks/Ruby.framework/Versions/2.0/usr/lib/ruby/2.0.0/rake/rake_test_loader.rb:9:in `block in <main>' from
/System/Library/Frameworks/Ruby.framework/Versions/2.0/usr/lib/ruby/2.0.0/rake/rake_test_loader.rb:4:in `select' from
/System/Library/Frameworks/Ruby.framework/Versions/2.0/usr/lib/ruby/2.0.0/rake/rake_test_loader.rb:4:in `<main>' rake aborted! Command failed with status (1): [ruby -w -I"lib" -I"/System/Library/Frameworks/Ruby.framework/Versions/2.0/usr/lib/ruby/2.0.0" "/System/Library/Frameworks/Ruby.framework/Versions/2.0/usr/lib/ruby/2.0.0/rake/rake_test_loader.rb" "test/**/*Tests.rb" ]
Tasks: TOP => default => test (See full trace by running task with --trace)
I thought that must might be a part of minitest, so I required 'minitest/unit' instead, but I still get an error. I also assume that must keyword isn't part of rspec, which I'm not using yet.
How do I get this to work properly?
It looks like that method is not provided out of the box, but was developed by a third party. You need to add code described here.

Getting an error when using "require" in ruby

I just started Ruby. This is what I tried:
require'F:\RubymineProjects\practice122013\Coordinatev2'
class XYZCoordinate < Coordinate
attr_accessor :z
##newtotal=0
def initialize(x,y,z)
super(x,y)
#z=z
##newtotal+=1
end
def to_s
return "(##x, ##y, ##z)"
end
def XYZCoordinate.total
return "Number of 3D-coordinates are: ###newtotal"
end
end
p1=XYZCoordinate.new(0,0,0)
puts p1.to_s
p2=XYZCoordinate.new(1,5,5)
puts p2.to_s
puts XYZCoordinate.total
and this is the error I get:
C:\Ruby193\bin\ruby.exe -e $stdout.sync=true;$stderr.sync=true;load($0=ARGV.shift) F:/RubymineProjects/practice122013/XYZCoordinate.rb
F:/RubymineProjects/practice122013/XYZCoordinate.rb:3:in `<top (required)>': uninitialized constant Coordinate (NameError)
(0, 0)
from -e:1:in `load'
2
from -e:1:in `<main>'
3
Can anyone help me please.
This is what I did to correct the problem.
require"F:\\RubymineProjects\\practice122013\\Coordinatev2"
require"F:\\RubymineProjects\\practice122013\\Coordinate"
I had to specify where those 2 files were.

Resources