Unable to Create a Ruby Hash inside a Class - ruby

I am trying to create a simple Ruby class but I am getting stuck. This is my code:
#!/usr/bin/ruby
class Dock
#ships = Hash.new(false)
def initialize()
end
def store(ship, pier)
#ships[pier] = ship
end
end
yathi = Dock.new
yathi.store("test", 12)
But when I try to run this by running this in Terminal:
ruby test.rb
This is the error message I am getting:
test.rb:8:in `'store': undefined method `'[]=' for nil:NilClass (NoMethodError)
from test.rb:13
It does work when if I rewrite it like this:
#ships = {pier => ship}
But this creates a new hash everytime with just one value which I don't want. Can someone please tell me what I am doing wrong?

Put the #ships = Hash.new(false) inside the initialize method. When you put it outside the initialize method you are defining a class level instance variable instead. Check out this writeup if you want to know more.

Try initializing the hash inside the "initialize" method so that it is a member of instances of the Dock class instead of a member of the Dock class itself:
class Dock
def initialize()
#ships = Hash.new(false) # <-- Define #ships as an instance variable.
end
def store(ship, pier)
#ships[pier] = ship
end
end

Related

Ruby - constant initialisation with some function produces NoMethodError

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

Why am I getting a NoMethodError when calling an instance method from global scope?

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'

How to Initialize Class Arrays in Ruby

I want to create an empty array as a class instance variable in Ruby. However, my current method does not seem to work.
Here is my code:
class Something
#something = []
def dosomething
s = 5
#something << s
end
end
When I call the function, it gives me an undefined method traceback.
However, if I do something similar with class variables, i.e.:
class Something
##something = []
def dosomething
s = 5
##something << s
end
end
This works perfectly.
I know I can use the initialize method to actually create an empty list for #something, but is there another way of doing this without using the initialize method? And why does this work for class variables?
EDIT: Fixed typo
You need to use initialize as a constructor as below code and is there any reason why not to use initialize/constructor. And please fix a typo error in class definition Class Something to class Something no camel case or first letter capitalize while in class
class Something
def initialize
#something = Array.new
end
def dosomething
s = 5
#something << s
end
end
class variable ## are available to the whole class scope. so they are working in the code and if you want to use instance variable # you need to initialize it as above. The instance variable is share with instance/objects of a class
for more details visit the link Ruby initialize method
At first you have a typo. Change Classto class. Next I suggest to use the initialize method. While creating a new object this is the perfect place to initialize instance variables.
class Something
##my_class_variable = [1]
def initialize
#something = []
end
def dosomething
s = 5
#something << s
end
def self.get_my_class_variable
##my_class_variable
end
end
Your script will be read and executed from top to bottom and after this,
you can access the class Something. While the parser reads your script/class/module you can define class variables (##), execute mixins and extend the class with other modules. This is why you can define a class variable, but you can not define an instance variable. Because actually you have no instance object from your class. You only have a class object. In ruby everything is an object. And your class object has a defined class variable now:
Something.get_my_class_variable
# => [1]
Now you can create an instance from your class. With Something.new the initialize method will be invoked and your instance variable will be defined.
something = Something.new
something.dosomething
# => [5]
Later, if you are familar with this you can define getter and setter methods with attr_reader, attr_writer and attr_accessor for instance objects or cattr_reader, cattr_writer and cattr_accessor for class objects. For example:
class Something
attr_reader :my_something
def initialize
#my_something = []
end
def dosomething
s = 5
#my_something << s
end
end
something = Something.new
something.my_something
# => []
something.dosomething
# => [5]
something.my_something
# => [5]
Your problem in trying to access #something in your instance method is that, in the scope of instance methods, # variables refer to instance variables, and your #something is a class instance variable.
# variables are instance variables of the instance that is self when they are created. When #something was created, self was the class Something, not an instance of Something, which would be the case inside an instance method.
How then to access a class instance variable in an instance method? Like regular instance variables, this must be done via a method, as in attr_accessor. One way to do this is to use class << self to tell the Ruby interpreter that the enclosed code should be evaluated with the class (and not the instance) as self:
class C
#foo = 'hello'
class << self
attr_accessor :foo # this will be a class method
end
def test_foo # this is, of course, an instance method
puts self.class.foo # or puts C.foo
end
end
We can show that this works in irb:
2.3.0 :005 > C.foo
=> "hello"
2.3.0 :006 > C.new.test_foo
hello
You have correctly created a class instance variable, #something, and initialized it to an empty array. There are two ways for instances to obtain or change the value of that variable. One is to use the methods Object#instance_variable_get and Object#instance_variable_set (invoked on the class):
class Something
#something = []
def dosomething
s = 5
self.class.instance_variable_get(:#something) << s
end
end
sthg = Something.new
sthg.dosomething
Something.instance_variable_get(:#something)
#=> 5
The other way is to create an accessor for the variable. There are several ways to do that. My preference is the following:
Something.singleton_class.send(:attr_accessor, :something)
Something.something #=> [5]
In your dosomething method you would write:
self.class.something << s

My class is calling a non-existing class?

I'm using:
Ruby 1.9.2
Rails 3.1.10
This is my code:
class Report::ExpectedHour
def initialize(user, options = {})
#user = user
#date_start = options[:start]
#date_end = options[:end]
end
def expected_hours_range
previous = ExpectedHour.previous_dates(#user, #date_start).first
hours_range = ExpectedHour.between_dates(#user, #date_start, #date_end)
unless hours_range.include?(previous)
hours_range << previous
end
hours_range
end
end
Every time I call expected_hours_range from my instance I get this error:
NameError: uninitialized constant Report::ExpectedHour::ExpectedHour
from /home/edelpero/.rvm/gems/ruby-1.9.2-p180#titi/gems/aws-s3-0.6.2/lib/aws/s3/extensions.rb:206:in `const_missing_from_s3_library'
from /opt/lampp/htdocs/titi/app/models/report/expected_hour.rb:10:in `expected_hours_range'
I'm not sure why Report::ExpectedHour::ExpectedHour is called because I'm calling ExpectedHour which is an actual existing ActiveRecord class. Also Report::ExpectedHour::ExpectedHour doesn't exist.
When calling classes inside your class methods, ruby expects it to be either a class nested inside you class itself or a constant. Try this:
class MyClass
def some_method
use_external_class = ::ExternalClass::CONSTANTB.bla
# Use the '::'
end
end

Accessing a class variable in Struct.new block

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.

Resources