Does above code mean @@states is frozen?
Yes (sort-of). You called .freeze on the object, so the object is frozen. I.e. technically the "frozen" thing is the object referenced by the variable, not the variable itself:
[1] pry(main)> x = "hello".freeze
=> "hello"
[2] pry(main)> x.frozen?
=> true
[3] pry(main)> x = "world"
=> "world"
[4] pry(main)> x.frozen?
=> false
Is @@states re-assignable then how?
There's no "public interface" to change the variable, but you can do it via meta-programming:
Test.class_variable_set('@@states', 'CHANGED!')
Is writing @states and @@states same in class methods?
No. There's a subtle difference between an instance variable in a class and a class variable. For example, see this answer for a more detailed explanation.
If @@states is not-frozen or re-assignable then how can I lock @@states once it is set so that it is not re-assignable?
You can't make it impossible to change/reassign anything in ruby; the best you can do is make it very awkward -- i.e. you have to write something "hacky" to change it.
In other words, you can remove any "public interface" and make the object frozen (which is exactly what you've done already), but - by the nature of ruby being a dynamic language with powerful meta-programming - there's always going to be some back-door way of changing a variable if you're really determined. For instance, see this library.
This isn't unique to ruby -- for example, it's possible to change constants in C/C++ by using pointers.