Rails: difference between self.function and self.class.function - ruby-on-rails

I am not sure I understand why I get a NoMethodError exception if I replace "var3 = self.class.function2" with "var3 = self.function2"
When should I use self and when should I use self.class ?
class TestClass
var1 = nil
def self.function1
var1 = "hello"
end
def self.function2
p self.function1
var2 = "world"
end
def function3
var3 = self.class.function2
p var3
end
end
myvar = TestClass.new
myvar.function3

def self.function2 is your class method that can not be called with instance of class. It call like TestClass.function2. While def function3 is instance method. You are calling it right.
myvar = TestClass.new
myvar.function3
So if we follow flow. Under function3 self will become instance that is calling that function.
var3 = self.class.function2 # Here self is myvar
# self.class == TestClass
# self.class.function2 == TestClass.function2 # No error
# self.function2 == myvar.function2 # error, Calling class method with instance of class.
Now if you try to call class method with instance it will surely through error.

Because the def self.function2 means, the function2 is "wired" to the class (TestClass) not to the instance of class (TestClass.new return instance of class TestClass). The call self.class returns the class (TestClass) where is the function2 based.
The function2 can be also called this way: TestClass.function2

Related

Passing variables between classes in Ruby on Rails

So I have two files, one called a.rb and one called b.rb. Here's the contents in both:
# a.rb
class A
def initialize
#variable = ""
#module_b = B.new(self)
end
def pass_to_b(self)
#module_b.do_something(#variable)
end
def set_variable(var)
# var = empty
#variable = var
end
end
and
# b.rb
class B
def initialize(module_a)
#module_a = module_a
end
def set_variable_in_a(data)
#module_a.set_variable(data)
end
def do_something(variable)
# variable = empty
set_variable_in_a("hello world")
end
end
This is just an example of what I'm dealing with. If I'm trying to start a function in Class A, which is supposed to do something in ClassB and then change an instance variable in Class A, I'm not sure how to do this properly. This is what I've tried, however:
a = A.new
a.pass_to_b
Class B cannot see the instance variable #variable, and if it tries to set_variable_in_a, that doesn't work either. It's like the do_something function in Class A successfully calls the do_something function in Class B, but the instance variable information is not available. I thought by passing self to Class B, we'd be able to at least call the function
My MRI throws exeption about
def pass_to_b(self)
because you can't pass self to method as argument.
You need delete 'self' how argument
Run code below and you will see that #variable of instance of Class A has '123hello world' string
class A
def initialize
#variable = "123"
#module_b = B.new(self)
end
def pass_to_b
#module_b.do_something(#variable)
end
def set_variable(var)
# var = empty
#variable = var
end
end
# b.rb
class B
def initialize(module_a)
#module_a = module_a
end
def set_variable_in_a(data)
#module_a.set_variable(data)
end
def do_something(variable)
set_variable_in_a(variable + "hello world")
end
end
a = A.new
a.pass_to_b
display variable 'a' and you will see something like this
#<A:0x00007fdaba0f3c90 #variable="123hello world", #module_b=#<B:0x00007fdaba0f3c40 #module_a=#<A:0x00007fdaba0f3c90 ...>>>

Class variable access from top level

I am a complete beginner to Ruby. I am working on Lesson 45 of Learn Ruby the Hard Way currently and am creating a game similar to Zork and Adventure.
I have created a structure where I am creating 'scenes' in different files and requiring all the scenes in one file where I have an engine/map that ensures if the current scene does not equal 'finished' that it runs 'X' scene's 'enter' method.
However I have two issues:
1) I keep getting a error saying 'Warning class variable access from top level'
2) Even though the script is running I get
ex45.rb:30:in `play': undefined method `enter' for nil:NilClass (NoMethodError) from ex45.rb:59:in
The following is all of my code from each file. My apologies if it's a long read, but I would love to know why I am getting these two errors and what I can do to fix them.
Ex45.rb:
require "./scene_one.rb"
require "./scene_two.rb"
require "./scene_three.rb"
##action = SceneOne.new
##action_two = SceneTwo.new
##action_three = SceneThree.new
class Engine
def initialize(scene_map)
#scene_map = scene_map
end
def play()
current_scene = #scene_map.opening_scene()
last_scene = #scene_map.next_scene('finished')
while current_scene != last_scene
next_scene_name = current_scene.enter()
current_scene = #scene_map.next_scene(next_scene_name)
end
current_scene.enter()
end
end
class Map
##scenes = {
'scene_one' => ##action,
'scene_two' => ##action_two,
'scene_three' => ##action_three
}
def initialize(start_scene)
#start_scene = start_scene
end
def next_scene(scene_name)
val = ##scenes[scene_name]
return val
end
def opening_scene()
return next_scene(#start_scene)
end
end
a_map = Map.new('scene_one')
a_game = Engine.new(a_map)
a_game.play()
scene_one.rb:
class SceneOne
def enter
puts "What is 1 + 2?"
print "> "
answer = $stdin.gets.chomp
if answer == "3"
puts "Good job"
return 'scene_two'
else
puts "try again"
test
end
end
end
scene_two.rb
class SceneTwo
def enter
puts "1 + 3?"
print "> "
action = $stdin.gets.chomp
if action == "4"
return 'scene_three'
else
puts "CANNOT COMPUTE"
end
end
end
scene_three.rb
class SceneThree
def enter
puts "This is scene three"
end
end
Thanks in advance!
Answer to your first question:
You need to move the class variable definitions inside your Map class to get rid of these warnings:
Ex45.rb:5: warning: class variable access from toplevel
Ex45.rb:6: warning: class variable access from toplevel
Ex45.rb:7: warning: class variable access from toplevel
So, your Map class would look like this:
class Map
##action = SceneOne.new
##action_two = SceneTwo.new
##action_three = SceneThree.new
##scenes = {
'scene_one' => ##action,
'scene_two' => ##action_two,
'scene_three' => ##action_three
}
def initialize(start_scene)
#start_scene = start_scene
end
def next_scene(scene_name)
val = ##scenes[scene_name]
return val
end
def opening_scene()
return next_scene(#start_scene)
end
end
To answer your 2nd question:
You are getting undefined method 'enter' for nil:NilClass (NoMethodError) because your current_scene becomes nil at some point and then you try to call: current_scene.enter() i.e. nil.enter and it fails with that error message.
To solve this problem, you have to make sure you always have some value in your current_scene i.e. make sure it's not nil.
I think, you can just remove current_scene.enter() line from the end of your play method in the Engine class. So, your Engine class will look like this:
class Engine
def initialize(scene_map)
#scene_map = scene_map
end
def play()
current_scene = #scene_map.opening_scene()
last_scene = #scene_map.next_scene('finished')
while current_scene != last_scene
next_scene_name = current_scene.enter()
current_scene = #scene_map.next_scene(next_scene_name)
end
# current_scene.enter()
end
end
And, you won't get that error anymore.
Just so you know:
##y = 20
p Object.class_variables
--output:--
1.rb:1: warning: class variable access from toplevel
[:##y]
And:
class Object
def self.y
##y
end
end
puts Object.y
--output:--
20
But:
class Dog
##y = "hello"
def self.y
##y
end
end
puts Dog.y #=>hello
puts Object.y #=>What do you think?
The output of the last line is the reason that class variables are not used in ruby. Instead of class variables, you should use what are known as class instance variables:
class Object
#y = 10 #class instance variable
def self.y
#y
end
end
puts Object.y
class Dog
#y = "hello"
def self.y
#y
end
end
puts Dog.y #=> hello
puts Object.y #=> 10
A class instance variable is just an #variable that is inside the class, but outside any def. And instead of there being one ##variable that is shared by all the subclasses, each subclass will have its own #variable.

Access varible in ruby after initialize

I am trying to access variable in ruby after initialize, but i didn't get that variable , anything wrong in that?
class Test
def initialize(params)
#has_test = params[:has_test]
#limit_test = params[:limit_test]
end
def self.method1(params)
Test.new(params)
#can i get that two instance variable
end
end
You should probably set up attribute accessors, then use them this way:
class Test
attr_accessor :has_test
attr_accessor :limit_test
def initialize(params)
#has_test = params[:has_test]
#limit_test = params[:limit_test]
end
def self.method1(params)
t = Test.new(params)
// can i get that two instance variable
// Yes:
// use t.has_test and t.limit_test
end
end
You are mixing an instance and a class method in your example.
If this is really what you want, then you have to define an accessor with attr_reader:
class Test
def initialize(params)
#has_test = params[:has_test]
#limit_test = params[:limit_test]
end
attr_reader :has_test
attr_reader :limit_test
def self.method1(params)
obj = Test.new(params)
p obj.has_test
p obj.limit_test
end
end
Test.method1(has_test: 1, limit_test: 3)
It the instance/class-method is a mistake, then this example may help you:
class Test
def initialize(params)
#has_test = params[:has_test]
#limit_test = params[:limit_test]
end
def method1()
p #has_test
p #limit_test
end
end
obj = Test.new(has_test: 1, limit_test: 3)
obj.method1
If you define also the accessors like in the first code, then you have again access from outside the class.
Just in case you don't want a reader, see also Access instance variable from outside the class

NoMethodError: undefined method `add_a_file' for Testmethod:Class

What is causing this error? It doesn't matter whether or not self. is specified.
class Testmethod < ApplicationController
def add_a_file(files, next1, f)
true
end
next1 = 0
files = Array.new
Dir.chdir("db/testdir")
Dir["*.xls*"].each do |f|
self.add_a_file(files, next1, f) # Error thrown here.
next1 += 1
end
Dir["*.csv*"].each do |f|
self.add_a_file(files, next1, f)
next1 += 1
end
sfiles = files.sort { |first, second| first[1] <=> second[1] } # ASC by mdate
puts sfiles.inspect
end
You try to call add_a_file method on Testmethod class (Testmethod.add_a_file) instead of on its instance. Either you should create this class' instance with:
instance = self.new
or make add_a_file a singleton method (class method):
def self.add_a_file(files, next1, f)
true
end

Creating a class method with Ruby problems

Why does the following code result in the error 'undefined local variable or method `foo_client' for Foo::People:Class'
class Foo::People
class << self
def get_account_balance(account_num)
foo_client.request :get_account_balance, :body => {"AccountNum" => account_num}
end
end
def foo_client
##client ||= Savon::Client.new do|wsdl, http|
wsdl.document = PEOPLE_SERVICE_ENDPOINT[:uri] + "?WSDL"
wsdl.endpoint = PEOPLE_SERVICE_ENDPOINT[:uri]
end
end
end
def get_account_balance is inside the class << self block, so it's a class method. def foo_client is not, so it's an instance method. So you can't call foo_client from get_account_balance because you're not calling it on an instance of People.

Resources