Let's consider following code:
class Try
CONST = xxx("42")
private_class_method def self.xxx(str)
str.to_i
end
end
puts Try::CONST
It produces an error: undefined method `xxx' for Try:Class (NoMethodError)
It seems that I cannot use a class private method to initialise a constant. The example is doing nothig actually, but it is a reproduction of an error I am facing trying to read data from file into the class constant.
Is it becuse ruby tries to initialize a constant before it get know about all methodes, or am I doing something wrong?
Not really. What you can't do is to assign the value of something that still hasn't been defined to something else in order to hold its value.
It has nothing to do with the method visibility because this works:
class Try
private_class_method def self.xxx(str)
str.to_i
end
CONST = xxx("42")
end
p Try::CONST
# 42
Related
I have searched around for the answer to this and I can see a lot of similar problems but I still do not understand what I am doing wrong here. I have declared a Ruby class and attempted to new it and then call some instance methods on the instance, so why do I get the NoMethodError on my start method?
class MyClass
def initialize
self.class.reset
end
def self.reset
...
end
def self.start(port)
...
end
end
test = MyClass.new
test.start '8082' <- here <- undefined method `start' for #<MyClass:0x2f494b0> (NoMethodError)
As you can see I am a Ruby noob. Any help would be appreciated. I can change my class structure but I would really like to understand what I am doing wrong here.
here start is a class method.
By your current approach, you can use it in the following way
MyClass.start '8080'
But if you want to use it on instance of class then use the following code
class MyClass
def initialize
self.class.reset
end
def self.reset
...
end
def start(port)
...
end
end
test = MyClass.new
test.start '8080'
You are using start as a Class variable, the method names preceded with self-keyword make those methods as Class methods. So if you really want to not change your class then you should call it like this:
MyClass.start '8080'
Else you can remove the self from your reset and start methods and make them as Instance methods and use them as:
test = MyClass.new
test.start '8082'
I am executing a class with only this code in rubymine:
def saythis(x)
puts x
end
saythis('words')
It returns an error: undefined method `saythis', rather than printing the string 'words'. What am I missing here? Replicating this code in irb prints the string 'words'.
I assume you wrote a class like the one below and did not write that code into a irb console. The problem is that you define an instance method, but try to call the method from the class level.
class Foo
def say_this(x) # <= defines an instance method
puts x
end
say_this('words') # <= calls a class method
end
There a two ways to "fix" this:
Define a class method instead of an instance method: def self.say_this(x)
Call the instance method instead of the class method call: new.say_this(x)
So why is this happening? It has to be a namespace error, I just don't understand where it is. I add a method to Fixnum like so in a file file.rb
module M
class Fixnum
def foo
return true
end
end
end
then I'll make a test like so:
require 'minitest/autorun'
require './file.rb' #the path is correct
class SomeTest < MiniTest::Test
def test_foo
assert 3.foo
end
end
which will in turn throw a
NoMethodError: undefined method `foo' for 3:Fixnum
when I run the test, and I am left scratching my head - even if I include M to include the module (applying the namespace?) for the test it still throws the error. I can use custom classes just fine, it's only when I try to add a method to an existing "open class".
Yes, you have defined your own M::Fixnum class which actually has nothing to do with ::Fixnum in the global namespace. The following will solve an issue:
module M
class ::Fixnum
def foo
return true
end
end
end
5.foo
#⇒ true
Please note, in the code above module M has no sense, since the code nevertheless monkey-patches the global Fixnum. The code is here just to show how you would monkey-patch the global class from inside another module code.
Plus, Ruby2 introduced refinements, which are likely what you are intended to use.
I have the following block of Ruby code within a class:
def self.blacklisted_keywords
%w(acquista acquistiamo acquisto acquistano compro compriamo comprano)
end
private
def item_valid?
keywords = blacklisted_keywords
end
Why can't I call blacklisted_keywords without getting: "undefined method `blacklisted_keywords'"? What am I doing wrong?
Because blacklisted_keywords is not an instance method, rather a class method.keywords = blacklisted_keywords means ruby looking at it implicitly as keywords = self.blacklisted_keywords. That causes an error, as it is not an instance method. Replace keywords = blacklisted_keywords as keywords = self.class.blacklisted_keywords
Following the answers provided, maybe it would make sense to keep the keywords in a constant if you don't need to mutate them.
class Blah
BLACKLISTED_KEYWORDS = %w(acquista acquistiamo acquisto acquistano compro compriamo comprano)
private
def item_valid?
keywords = BLACKLISTED_KEYWORDS
end
end
I'm using Struct.new to create new classes on the fly (we're using some entity modelling middleware, and I want to generate concrete types on the fly for serialization).
In essence I have this code:
module A
def self.init_on(target)
target.foo = 123
end
end
$base_module = A
module Test
C = Struct.new(:id) do
include $base_module
##base = $base_module
def initialize
##base.init_on(self)
end
attr_accessor :foo
end
end
c = Test::C.new
puts c.foo
I get this error when I run my test:
test2.rb:17:in initialize': uninitialized class variable ##base in Test::C (NameError)
from test2.rb:24:innew'
from test2.rb:24:in `'
From my understanding of Struct.new, the block is executed with the context of the class being created, so ##base should be resolvable.
Thanks for your time!
Edit:
Thanks - I made init_on self.init_on and used class_variable_set rather than instance_variable_set. It now works!
Why not try to use something like self.instance_variable_set(:##base, $base_module). I think that may work, since you are just setting an instance variable of the class object.