views:

1483

answers:

3

Hello,

I am facing a undefined local variable or method error when initializing the following in ruby:

 class Model
  attr_accessor :var1, :var2, :state
  def initialize (x, y, key)
    @var1 = x
    @var2 = y
    @state = every_state[:key] #this line produces the error

  @every_state = {
  :A => SateA.new,
  :B => StateB.new,
  :C => StateC.new,
  :D => StateD.new
  }       
  end

  def select_state(key)
    every_state[:key]
  end
end

When I am using the class like

model = Model.new(1,2,:A)

The error occurs: *undefined local variable or method `every_state'*

As I am new to ruby coming from a java background, I wanted to pass a key to the initialize method (as noted here), to select a specific initial state from the hash.

Could it be that I am using the hash in a wrong way, or should I take it out of the initialize method completely and use another method to set it? My other thought is that I am using the symbol for key incorrectly.

Also, is there a direct implication of operating on non-instance variables within the initialize method? For example I was wondering what is the purpose of declaring the hash as an instance variable within initialize...

Any ideas are very welcome.

+1  A: 

you have a typo: :A => SateA.new should be :A => StateA.new. Also I would put the hash definition at the top level outside of the initialize method otherwise you'll be redefining it with every call to new which is definite;y NOT want you want to do!

ennuikiller
Thanks for the hint, however would you mind explaining a little further. I put the has definition outside of the initialize in a separate method:def getStates available_states = { :A => SataeA.new, :B => StateB.new, :C => StateC.new, :D => StateD.new } endWouldn't that still redifine the hash members each time? Thanks
denchr
You are still spelling state wrong, `SataeA` needs to be `StateA`.
Garrett
fixed that. Thanks
denchr
I believe he wants to maintain a set of states for each individual instance, that's why i think the hash definition is ok as it's now.
khelll
+2  A: 

You don't have a function called "every_state". You only have a instance variable @every_state. That is why you get an error.

Replace all calls to every_state with @every_state. You don't have a function or a local variable every_state. You only have a instance variable @every_state.

hrnt
A: 

I believe this implementation is the closest one to the Java one.

class Model
  attr_accessor :var1, :var2, :state

  def initialize (x, y, key)
    @var1 = x
    @var2 = y
    @every_state = {
      :A => SataeA.new,
      :B => StateB.new,
      :C => StateC.new,
      :D => StateD.new
    }   
    @state = select_state key 
  end

  def select_state(key)
    @every_state[key]
  end
end
khelll
That works, thanks for the help. However I am still wondering about ennuikiller's remark: How can I avoid redefining the hash with every call to Model.new ?
denchr
Note that the latter version is different than the original Java version. The latter version shares the states with all instances (If you modify State A in instance X, it also gets modifies that state A of instance Y).
hrnt
So, now that the hash is defined as a constant, it is shared among all instances of Model.Each model carries the entire hash and also has an instance variable @state. @state selectively takes a value from the hash depending on the business logic. This *should* vary between different model objects, right?If the original constant hash with the states change, then the change will propagate to all model objects, right? So by saying "shares the states with all instances" you mean shares the hash which is now a constant across all models? -- but not the value of the @state in each of them.
denchr
Thanks, but I am still a bit confused: we modify the @state variable by taking any value from the States hash right? Is that what you mean?The way I understand it is that @state is instance-specific, but the States hash, which is now a constant, is tied to the class, thus shared among all instances. I hope this is correct. Perhaps I should read up on the clone method..
denchr
I really appreciate the explanation. The idea was for a Model to store its available states in a hash structure. All subsequent model objects should share the same available states.@state is a variable which references an object from the available states hash based on some business logic. Each state object in the hash has actions which may or may not change the @state of that particular instance.So @state is instance specific, but the hash structure, which can be defined as a constant, is tied to the class? -- Still some confusion.. Sorry about all the questions.
denchr
I have rolled out the update after reading the Java code carefully cause it's not what you want. Back to your first question, the redefinition here has no side effect. I believe ennuikiller's remark is not valid in this context.
khelll
...so it makes no difference where I define the hash? Inside the initialize method as an instance variable, or at top level as a constant..
denchr
In ruby, defining the constant inside the class body(top level) will make it class constant, like doing public static final in Java, and following this approach will make us share the state as hrnt noted above. That's not what you want for sure and that's why I represented the states as instance variable instead.
khelll
Well, actually that is what I had in mind... Could it be that my java example is wrong then, based on my comments above?
denchr
No it's ok, but if you turned the everyState constant to a normal field then you will have the same code as Ruby's one.
khelll