NoMethodError but did 'require_relative' - ruby

I did the 'requir_relative' but still got the NoMethodError.
There are 2 ruby files, under 'run.rb' I have this
class Run
def separate(data)
hash_block = []
(0...data.count).each do |i|
f = data[i].split('|')
hash_block[i] = Hashing.new(f[0].to_i, f[1], f[2], f[3], f[4])
end
hash_block
end
end
and then in the main file, I did these:
require_relative 'run'
...some codes...
to_separate = IO.readlines(ARGV[0])
separated = separate(to_separate)
...some codes...
but I still get this:
in `block in <main>': undefined method `separate' for main:Object (NoMethodError)
If I cut the method and paste it in the main file it will work as expected but that is something I wanted to avoid.

In order to call the method within the class Run you have to instantiate it. Since is an instance method. The way your calling the class is giving you the error undefined because it can not find it with in the scope of your current file
run_instance = Run.new
to_separate = IO.readlines(ARGV[0])
sperated = run_instance.separate(to_separate)

You required the file, but in that file you have a class definition. separate is inside that class (and that's an instance method), so you need an object to call the method on.
separated = Run.new.separate(to_separate)

Related

Undefined local variable or method `translator' for main:Object (NameError)

I've used a gem and tried to create a method (trans) in my code.
require 'yandex-translator'
translator = Yandex::Translator.new(api_key)
def trans(text)
a = translator.translate text, to: "ru"
return a
end
puts trans("stack")
When I run the code, I get this error:
'trans': undefined local variable or method `translator' for main:Object (NameError)
Why did I get this error, and how can I solve this?
translator variable in this code is defined on class level, hence it’s a local variable in main context (since the whole code is executed in main context.)
You are trying to call it from the instance context, where it is obviously not defined. The easiest way to overcome it, would be to define #translator as being a class’ instance variable:
#translator = Yandex::Translator.new(api_key)
def trans(text)
#translator.translate text, to: "ru"
end
Because in this way you are looking for a local variable translator and you have not. Some solutions:
make translator global
$translator = Yandex::Translator.new(api_key)
or pass translator to trans method
def trans(translator, text)
translator.translate text, to: "ru"
end

Ruby unknown + on trying to join 2 strings

So, I'm learning Ruby and immediately, have stumbled upon something rater peculiar when trying to concatenate 2 strings to one. Here's the code, with irrevelant parts stripped, lets just say Sinatra runs it:
class CMS
# Set the site path root.
#sitePath = "./site"
get '/' do
renderCache = File.readlines(#sitePath + "index.liquid")
end
end
And on loading the page, I am greeted with
NoMethodError at /
undefined method `+' for nil:NilClass
on the renderCache = File.readlines(#sitePath + "index.liquid") line. Why is it refusing to concatenate the strings?
You can't set instance variables at the class level. You need to set them in an instance method.
Look's like you're using sinatra so you can do this:
See here for how to make a "before filter" like one does in Rails apps. This solution is for the modular style of Sinatra app.
To show an example:
class CMS < Sinatra::Base
before do
#sitePath = "./site"
end
get '/' do
renderCache = File.readlines(#sitePath + "index.liquid")
end
end
CMS.run!
You could also keep your existing code if you use a constant instead of an instance variable:
class CMS
# Set the site path root.
SitePath = "./site"
get '/' do
renderCache = File.readlines(CMS::SitePath + "index.liquid")
end
end
To explain how I read your error and looked for the error:
undefined method '+' for nil:NilClass means you're calling + on something which is nil. Referencing the code shows that the nil variable is #sitePath. Undefined instance variables will evaluate to nil. This is different than standard variables, which will raise an undefined variable error.

Parent class's instance variable in Ruby

So, for whatever reason there is no peek method in the ruby core Queue class. I am trying to create a child class that implements the peek method. However, I don't understand why I am getting an error. Is it not possible to use instance variables in this way? Looking at the source code for Queue, there are instance variables in the constructor of the parent class. Is there a way to reference these in the subclass?
class PeekQueue < Queue
def peek
#mutex.synchronize{
while true
if #que.empty?
raise ThreadError, "queue empty" if non_block
#waiting.push Thread.current
#mutex.sleep
else
return #que[0]
end
end
}
end
end
a = PeekQueue.new
a.push(1)
a.peek
NoMethodError: undefined method 'synchronize' for nil:NilClass
Edit: The Queue class is created at compile time, which is why I couldn't find the source on the ruby source code on github. This is what the parent class looks like:
https://gist.github.com/anonymous/574e20fea3a28663bfe2
I do not see that error:
irb(main):025:0> qq = PeekQueue.new
=> #<PeekQueue:0x000006002bf498 #que=[], #num_waiting=0, #mutex=#<Mutex:0x000006002bf420>, #cond=#<ConditionVariable:0x000006002bf3f8 #waiters={}, #waiters_mutex=#<Mutex:0x000006002bf3a8>>>
irb(main):026:0> qq.peek
NameError: undefined local variable or method `non_block' for #<PeekQueue:0x000006002bf498>
from (irb):15:in `block in peek'
from (irb):12:in `synchronize'
from (irb):12:in `peek'
from (irb):26
from /usr/bin/irb:12:in `<main>'
irb(main):027:0> qq.push 1
=> #<ConditionVariable:0x000006002bf3f8 #waiters={}, #waiters_mutex=#<Mutex:0x000006002bf3a8>>
irb(main):028:0> qq.peek
=> 1
Method #non_block seems to be an issue. But access to #mutex works with your code.

How to load file in object context

I'm playing with some meta-programming concepts and wonder if something I want to do is simply possible.
There's simple DLS for events,
//test_events.rb
event 'monthly events are suspiciously high' do
true
end
and the script should shout out when event returns true, I try to do this without polluting global namespace with method event, and any instance variables. So I try something like this:
Dir.glob('*_events.rb').each do |file|
MyClass = Class.new do
define_method :event do |name, &block|
#events[name] = block
end
end
env = MyClass.new
env.instance_eval{#events = {}}
env.instance_eval{load(file)}
end
So for each *_events.rb file I would like to load it in context of MyClass (i know that with 2nd loop of Dir.glob#each it will complain about already defined const - not important now).
The problem is with env.instance_eval{load(file)} code in test_events.rb is run in Object context, because I get
undefined method `event' for main:Object (NoMethodError)
Is there a way to do it? ( I try now in 1.9.3 but changing version up is not a problem since it's just exercise)
instance_eval can take a String as its argument instead of a block, so rather than load (which as you suggest will load the file in the top level) you need to read the file contents into a string to pass in, something like:
env.instance_eval(File.read(file))

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.

Resources