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.
Related
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)
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
When I try to run this code:
class Message
##messages_sent = 0
def initialize(from, to)
#from = from
#to = to
##messages_sent += 1
end
end
my_message = Message.new(chicago, tokyo)
I get an error that tells me that one of my parameters is undefined local variable. I was just trying to create an instance using Message, and was curious as to why this was not working. I thought this would work as I am calling the class.
Problem
With your current code, you get this error:
undefined local variable or method `chicago' for main:Object (NameError)
because the way you instantiated the Message class:
my_message = Message.new(chicago, tokyo)
chicago and tokyo are interpreted as variable or method that you did not actually define or declare, that's why you got that error.
Solution
I think, you just wanted to pass two string objects (putting chicago and tokyo in the quotes) as the arguments of Message.new call like this:
my_message = Message.new('chicago', 'tokyo')
This will solve your problem.
Hope this makes it clear why you are getting the error and how to solve the problem.
The 'undefined local variable' error is showing up because there's no value associated with chicago or tokyo. If you want to just pass them as strings, wrap them in quotation marks instead, like this:
class Message
##messages_sent = 0
def initialize(from, to)
#from = from
#to = to
##messages_sent += 1
end
end
my_message = Message.new("chicago", "tokyo")
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.
I am trying to make use of Rail's distance_of_time_in_words helper but I'm getting an Undefined Method Error for some reason. Here's my code:
def confirm_has_quota
last_upload = current_user.photos.last.created_at
remaining_time = distance_of_time_in_words(1.day.ago, last_upload)
if last_upload < 1.day.ago
return true
else
flash[:error] = "You are allowed 1 upload per day. Please try again in" + remaining_time + "."
redirect_to(:back)
end
end
Which gives me "undefined method `distance_of_time_in_words'". Anyone see what I'm doing wrong here? Thanks.
The distance_of_time_in_words method is an ActionView Helper, thus needs to be called from the View (not the controller).
http://api.rubyonrails.org/classes/ActionView/Helpers/DateHelper.html
Or you can access the same through the view_context which is available inside the controllers.
view_context.distance_of_time_in_words