I've been tooling around with Ruby by converting a pen and paper RPG to a script.
Right now I have a character's stats kept in a hash, which I would like to be able to set via public method. I got that working with:
class Character
attr_reader :str, :con, :dex, :wis, :int, :level, :mods, :stats
def initialize str, con, dex, wis, int, cha, level = 1
@stats = { :str => str, :con => con, :dex => dex, :wis => wis, :int => int, :cha => cha }
@mods = {}
@level = level
@stats.each_pair do |key, value|
@mods[key] = ((value / 2 ) -5).floor
end
end
def []=(index, value)
@stats[index] = value
end
end
This allows me to instantiate a new character, then update @stats
by running newChar.stats[:str] = 12
However, I also seem to be able to modify the @mods
using this method as well, which is undesirable. newChar.mods[:str] = 15
will successfully alter the @mods
hash, which from my understanding should not be possible with the current setter method.
On a slightly separate note, the iterator I'm using to create my @mods
hash seems clunky but I haven't found anything better to accomplish the task.
You did not even call your
[]=
method in your example. This would be done like so:instead of
so to call
newChar.stats[:str] = 123
you do not even need you method definition. The reason is thatnewChar.stats
as well asnewChar.mods
will both return the actual hash which can then be altered.One possible workaround is to freeze the
@mods
variable so it can't be altered any more:This is a good solution if you never want to be able to change
@mods
again. Trying to set a value will result in an error:Inside your class, you can, however, overwrite
@mods
entirely.To summarize, the full class would be:
If you need a public getter for a hash but you don't want the user to modify the hash –the instance-variable of your class–, you can do it with dup.
Where the solution with
freeze
, as mentioned above, will freeze the hash even for your class, the .dup-solution will let you modify the hash from within your class but not from outside.