using attributes of different objects in different objects methods - ruby

what i need is basically to use variable from one file, in the method. let me explain
lets say we have
class Game
attr_accessor :number, :object
end
where number is just some number and object is object of some other class defined by me, lets name it Player class. now we make another file, which requires class Game, and which goes like this:
require './Game.rb'
require './Player.rb'
myGame = Game.new
myGame.number = 1
myGame.object = Player.new
and now the big moment. in method defined in Player class, i would like to use myGame.number attribute. eg like this
class Player
attr_accessor :some_var
def method
#some_var = myGame.number
end
end
How can i achieve this?

Your player should have a reference to the game is playing. For instance
class Game
attr_accessor :number
attr_reader :my_player
def my_player=(player)
player.my_game = self
#my_player = player
end
end
class Player
attr_accessor :some_var, :my_game
def method
#some_var = #my_game.number if #my_game
end
end
myGame = Game.new
myGame.number = 1
myGame.my_player = Player.new()
myGame.my_player.method
puts myGame.my_player.some_var

Alternatively to toch's answer, you can keep a reference to the game object when you set the player accessor. Instead of using automatic accessors you can use the get_ set_ accessor syntax to have custom code in the accessor, which would set the reference on the rvalue.

Related

Ruby variable not accessible in classes

I'm trying to pass a variable to numerous classes but get an 'undefined local variable or method' error.
I've created a 'Player' class which has a method to initialise an object with two parameters, and have a 'Board' class where I want to use one of these parameters (I've just included a 'puts' statement for simplicity below) but this is where the error occurs. Outside of the 'Board' class, the same statement works (currently commented out).
How can I use the player1.name value inside of the 'Board' class please? Thanks
class Player
attr_accessor :name, :symbol
def initialize(name, symbol)
#name = name
#symbol = symbol
end
end
class Board
puts player1.name
end
player1 = Player.new("Player1","X")
#puts player1.name
When you open a class with the class keyword you open a new scope and the current bindings are thrown out of the window. Furthermore your code would never work since you reference player1 before defining the variable.
To solve these two issues we have to move the player1 definition above the class. Then instead of using the class keyword to define Board class we can use the Class constructor which keeps the current bindings.
This results in:
player1 = Player.new("Player1","X")
Board = Class.new do
puts player1.name
end
Although this might solve your problem, I doubt that this is the functionality you'll actually need. The question reeks of a XY-problem. The above will bind the class as a whole to what the player1 value is at the moment of class definition.
In turn this means that every instance of Board is in some way bound to player1. I assume that every instance of Board can have its own player(s), in which case the following would be a better fit:
class Board
def initialize(player1)
#player1 = player1
end
end
It seems that you're trying to call the instance of player1 inside the Board class before it is created.
This means player1 does not exist in that context.
I guess you could either instantiate it inside the Board class or pass it as an option. Here's an example:
class Board
def initialize(options={})
# Uses the player passed to it.
#player = options[:player]
end
# Not often seen since you can get it from the player class
# but can be done here inside the Board class too if you want to.
def player_name
puts #player.name
end
end
# Instantiate your player as usual.
player1 = Player.new("Player1","X")
# Adding it to an options hash
# this step is optional, you can add it directly to board
# like you did for adding the name and symbol to the player class.
options = {player: player1}
# Instantiate a board with the player you've created.
board = Board.new(options)
# To puts the player name run the method.
board.player_name # "Player1"
See Ruby Classes and Modules if you would like more examples and learn more.

How to access variable from outside class in another file

My file structure is as follows:
Main/
Games/
roulette.rb
casino.rb
wallet.rb
player.rb
I have a wallet class that holds a money value in the class like so.
class Wallet
attr_accessor :money
def initialize
#money = 0
end
end
I then have a player class that inherits from the Wallet class
class Player < Wallet
attr_accessor :name
def initialize
super()
#name = nil
get_user_info
end
I then have a Casino class that inherits from Player like so
class Casino < Player
def initialize
binding.pry
puts #money, #name
end
I have also used require_relative to pull in both files thinking that would give me access to their global variables #money, #name.
If I am inside the roulette.rb file here is my code I wrote just to see if it would have a value.
require_relative '../wallet.rb'
class Roulette
def initialize
puts #wallet
end
end
How would I go about getting access to these variables in the casino class? Thanks for the help.
Those are not global variables. They are called "instance variables" and to access them you need to create instances of your casinos and players. Looks like this.
player = Player.new
player.money # => 0
player.money += 10
player.money # => 10
In your Casino class you don't call parent initializers (a simple oversight, I think), so it doesn't initialize #name and #money.
And Roulette doesn't do anything at all to obtain a wallet. So it stays at default value nil.

Understanding Ruby Setters

If I create a class like this:
class Player
def initialize(position, name)
#position = position
#name = name
end
end
Isn't that setting the name to an instance variable? if so, why would I need to write a setter like this
class Player
def initialize(position, name)
#position = position
#name = name
end
def name=(name)
#name = name
end
end
Basically when is it necessary to write getters in a class?
Getters and setters job is to provide you a quick implementation of read and write of instance variables that you define in your constructor:
class Player
attr_accessor :name, :position
def initialize(position, name)
#position = position
#name = name
end
end
you can also user attr_reader(for getters) and attr_writer(for setters) specifically for these variables.
Above code: attr_accessor :name, :position gives you: #name, #position, #name=, and #position= methods for instance of Player class.
However, they're not going to give you validation or a customized logic for getters/setters.
For example: you might want to show a player's full name or do not wish your code to accept a 0 or negative position, in such a case you'd have to write getter and setter yourself:
class Player
def initialize(first_name, last_name, position)
#first_name = first_name
#last_name = last_name
#position = position
end
# validation for updating position using setter of position
def position=(new_position)
raise "invalid position: #{new_position}" if new_position <= 0
#position = new_position
end
# customized getter for name method
def name
"#{#first_name} #{#last_name}"
end
end
If you do not require customization as stated above then using attr_* method for these variables makes more sense.
initialize sets attributes during the initialization of a new object.
keeper = Player.new('goalkeeper','Shilton').
What if you want to update an attribute of keeper after initialzation? Well you'll need to use your ordinary setter method:
def name=(name)
#name = name
end
like so:
keeper.name = 'Banks'
If you don't have a setter method defined for Player instances, then you can't do this. Similarly for getter methods. Also be aware you can refactor your code by using attr_accessor like so:
class Player
attr_accessor :name, :position
def initialize(position, name)
#position = position
#name = name
end
end
Getters/setters, also known as "accessors", are accessible outside the class, instance variables are not. If you want things to be able to read or change #name from outside the class, you to define accessors for it.
Additionally accessor methods allow you to perform a certain amount of sanity checking or mutate incoming/outgoing values, and otherwise protect the internal state of your objects.

Instantiating a class within a subclass and accessing its instance within another subclass

I have a class:
class Player
def initialize(name, npc=true)
#name = name
#npc = npc
end
end
and also a parent class and some subclasses:
class Scene
end
class Intro < Scene
joe = Player.new("Joe", false)
end
class NeighboursHouse < Scene
end
I create a new instance of Player within Intro, and I need to access that instance in NeighboursHouse too. Is there a way to do this without repeating in every subclass?
Just to clarify, I want to be able to create new Player instances within each Scene and access them in other scenes. They may be created at random based on differing outcomes so I won't necessarily be able to create them in the parent class.
This sounds like a program designing problem. If I were you, I would rather let the class Player to manage the players, and let the Scenes "invite" the players whenever it needs one.
class Player
#players = {}
def self.[](name, npc=true)
#players[name] ||= new(name, npc)
end
def initialize(name, npc=true)
#name = name
#npc = npc
end
end
And when you need a player, for example in Intro:
class Intro < Scene
joe = Player["Joe", false]
end
In this way, you don't need to worry about creating duplicated players.
The only setback is that there can't be 2 players with the same name but one is npc and the other is not.
The question is not clear, but I guess you can just create the instance in Scene, and refer to it from the subclasses.
class Scene
##joe = Player.new("Joe", false)
end
class Intro < Scene
##joe # => refers to the object
end
class NeighboursHouse < Scene
##joe # => refers to the same object
end
If the question is how to get some code snippet to run every time a class is inherited (but only once per inheritance), then you are looking for inherited:
class Scene
def self.inherited(subclass)
super
joe = Player.new("Joe", false)
end
end

Ruby: Variable initialization within classes

Having some trouble when it comes to initializing variables within a class (instance variables etc.) and I was wondering if anybody could clarify the proper syntax for me.
Sample code:
Class Pets
attr_accessor :name
def initialize(name)
#name=name
end
def name=(name)
#name = name
#I believe this is where I change #name instance variable
end
#in this space I could create more <methods> for Class.new.<method>
end
My question is do I need to have attr_accessor as well as def initialize and def name=?
In addition, if I have multiple attr_accessors do I need to add them as arguments to def initialize, e.g.:
Class Pets
attr_accessor :name :age :color
def initialize(name, age, color)
#name = name
#age = age
#color = color
#and if this is the case do I need methods for each (name= age= color= etc.)
end
One last thing:
If someone could confirm or deny my thought process on the name= age= and color= type of methods within the classes. Am I correct in thinking method= is necessary to change the instance variable? I am a bit unsure about what the method= is for and why I cannot change the instance variable within initialize.
attr_accessor :symbol do the same as attr_writer :symbol and attr_reader :symbol, i.e. it creates both reader (def symbol; #symbol; end) and writer (def symbol=(value); #symbol = value; end).
Initialize is a method called every time new instance of the class is being created. It is not the same as new method as some classes may have its own custom factory methods. You don't need to define your initialize method, only problem is that then symbol reader would return nil, as the local variable would not been set.
In ruby everything is a method. In case of objects, object.attr = value is just a short for object.attr=(value) where attr= is just another method. (Similarly << operator is defined as a method on Array class, attr_accessor is a method defined on class "Class").
To piggy back on what what said earlier, recall that if you want your attributes to be accessible outside your class (you want to write over the attribute value or you want to read it) you will need to use the attr_accessor (or attr_writer or attr_reader).
If I had a class like ...
class Calendar
attr_reader :event_name, :all_events
def initialize
#event_name = event_name
#all_events = []
end
def create_event(event_name)
puts "#{event_name} has been added to your calendar."
#all_events << event_name
p #all_events
end
def see_all_events
puts "Here are your events --"
#all_events.each {|event| puts "- #{event}"}
end
end
my_calendar=Calendar.new
my_calendar.create_event("interview")
my_calendar.see_all_events
my_calendar.all_events
I can read all my events either with the method see_all_events or by calling all_events on my class Calendar object. If for some reason I did not want a see_all_events method but instead only wanted it to be seen by calling all_events on my object I can only do this because of attr_reader.
Basically the point here is to remember exactly how you want your users to interact with your object attributes. If it needs to be private and only accessed via methods then you should be weary of using attr_accessor or attr_writer or attr_reader (depending on the situation).

Resources