Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Stack level too deep

Hello I am received a "Stack level too deep" error and I am fairly sure that it is being generated from this model. I know it has something to do with a recursive call, but I have thus far been unable to locate it, thanks.

class Character < ActiveRecord::Base

  # Associations
  belongs_to :user

  # Validations
  validates :name, :presence => true, :uniqueness => true, :length => 
  { minimum: 2, maximum: 20 }, format: { with: /\A[a-zA-Z]+\Z/ }
  validates :race, :presence => true
  validates :class, :presence => true
  validates :user, :presence => true

  def self.races
    ["Human", "Dwarf", "Elven", "Orc", "Undead", "Demon"]
  end

  def self.classes
    {
      :fighter => {strength: 4, endurance: 3, dexterity: -2, charisma: -2, wisdom: -2, intelligence: -3}, 
      :thief   => {strength: -3,endurance: 2, dexterity: 4, charisma: 2, wisdom: -2, intelligence: 0}, 
      :magi    => {strength: -3, endurance: -2, dexterity: -2, charisma: 2, wisdom: 3, intelligence: -3}, 
      :ranger  => {strength: -2, endurance: 2, dexterity: 2, charisma: 0, wisdom: -3, intelligence: 0}, 
      :cleric  => {strength: 2,endurance: 2, dexterity: -3, charisma: -2, wisdom: 3, intelligence: 2}
    }
  end

  def set_class(_class)
    _attributes = Character.classes[_class.downcase]
    transaction do
      self.class = _class.downcase
      _attributes.each do |name, value|
        self.name += value
      end
      self.save
    end
  end
end

Server log:

Started GET "/characters/new" for 127.0.0.1 at 2014-04-04 01:54:14 +0200
  [1m[36mActiveRecord::SchemaMigration Load (0.8ms)[0m  [1mSELECT "schema_migrations".* FROM "schema_migrations"[0m
Processing by CharactersController#new as HTML
  [1m[35mUser Load (1.5ms)[0m  SELECT "users".* FROM "users" WHERE "users"."id" = 1 ORDER BY "users"."id" ASC LIMIT 1
  Rendered shared/_error_messages.html.erb (3.8ms)
  Rendered characters/new.html.erb within layouts/application (38.1ms)
Completed 500 Internal Server Error in 199ms

SystemStackError - stack level too deep:

activerecord (4.0.2) lib/active_record/attribute_methods/read.rb:85:in `'
like image 482
manis Avatar asked Feb 13 '23 07:02

manis


2 Answers

There are three problems with your code:

  1. you are using class as an attribute,
  2. you are confusing :symbols and "String".downcase,
  3. you are using self.name (attribute) thinking it's a different setter.

Class

Don't use class. It's a reserved keyword.
These lines will leave ruby very confused:

validates :class, :presence => true

self.class = _class.downcase

Also, I assume that you have a class column in your DB table. You should really rename it to something like character_class or rpg_class.

Symbols and Strings

Your self.classes method returns a Hash whose keys are symbols. And yet, later you try to access it using a string:

_attributes = Character.classes[_class.downcase]

While it's true that :foobar.downcase will do nothing, you can't access those values with "Foobar".downcase.
To fix that, you could:

  • use Character.classes[_class.to_sym]
  • use an HashWithIndifferentAccess

In any case, that method could be improved by using memoization.
At the moment you are re-creating a hash of hashes with each call. Instead, you can save it in a variable and create it only the first time.

def self.classes
  @char_classes ||= { fighter: { #...
end

Or, even better, use a constant:

CHAR_CLASSES = { #.....

def self.classes
  CHAR_CLASSES
end

name

I see that you have this validation in place:

validates :name, :presence => true, :uniqueness => true, :length => { minimum: 2, maximum: 20 }, format: { with: /\A[a-zA-Z]+\Z/ }

which means you have a name column in your DB table, and that it's supposed to be a String matching a specific format (only letters, between 2 and 20).

With this said, let's have a look at this block of code:

_attributes.each do |name, value|
  self.name += value
end

In this local scope, name is a variable holding a Symbol (e.g. :strength) and value is a Fixnum.
When you do self.name += value, however, you are assigning the Fixnum to the object attribute name.

I can't see where you define methods like strength= or dexterity=. I'm assuming they are columns on the table.

In that case, then this should work:

self.public_send("#{name}=", value)
# or just 'send' if it's a private method
like image 79
tompave Avatar answered Feb 24 '23 15:02

tompave


When you are setting the attributes on your character, you need to generate the setter dynamically. You can't call a dynamic method by appending a variable. name = 'mike'; self.name will not call self.mike.

def set_class(_class)
  _attributes = Character.classes[_class.downcase]
  transaction do
    self.class = _class.downcase
    _attributes.each do |name, value|
      self.public_send("#{name}=", value)
    end
    self.save
  end
end
like image 27
jstim Avatar answered Feb 24 '23 17:02

jstim