0

Giving a Java example:

class MyClass {
  private final int variable;

  MyClass(int variable) {
    this.variable = variable;
  }

}

Is there something like final I can do in Ruby? I looked up freeze but am not sure if it's the right approach. I don't need the variable to be assigned while creating a new instance of my class, I need to set it in one of the methods and once it's set I want to freeze it.

the Tin Man
  • 158,662
  • 42
  • 215
  • 303
noMAD
  • 7,744
  • 19
  • 56
  • 94
  • "I need to set it in one of the methods and once its set I want to freeze it." So why can't you do this? Freeze is just a method call, so call it wherever you need it. – Max Feb 24 '15 at 17:27
  • Freeze doesn't seem to work when I tried it in irb. `a=20; a.freeze; a+=10;` then `p a` prints 30 – noMAD Feb 24 '15 at 17:29
  • Fixnums are immutable and thus unfreezable. You really want a constant here; constants aren't final in Ruby like they are in Java, but they are conventionally treated as such. – Chris Heald Feb 24 '15 at 17:46

3 Answers3

2

They are called constants. A constant in Ruby is defined by a UPPER_CASE name.

VARIABLE = "foo"

It is worth to mention that, technically, in Ruby there is no way to prevent a variable to be changed. In fact, if you try to re-assign a value to a constant you will get a warning, not an error.

➜  ~  irb
2.1.5 :001 > VARIABLE = "foo"
 => "foo"
2.1.5 :002 > VARIABLE = "bar"
(irb):2: warning: already initialized constant VARIABLE
(irb):1: warning: previous definition of VARIABLE was here
 => "bar"

It's also worth to note that using constants will warn you if you try to replace the value of the constant, but not if you change the constant value in place.

2.1.5 :001 > VARIABLE = "foo"
 => "foo"
2.1.5 :002 > VARIABLE.upcase!
 => "FOO"
2.1.5 :003 > VARIABLE
 => "FOO"

In order to prevent changes to the value referenced by the constant, you can freeze the value once assigned.

2.1.5 :001 > VARIABLE = "foo".freeze
 => "foo"
2.1.5 :002 > VARIABLE.upcase!
RuntimeError: can't modify frozen String
    from (irb):2:in `upcase!'
    from (irb):2
    from /Users/weppos/.rvm/rubies/ruby-2.1.5/bin/irb:11:in `<main>'
2.1.5 :003 > VARIABLE
 => "foo"

Here's an example inside a class.

class MyClass
  MY_CONSTANT = "foo"
end

MyClass::MY_CONSTANT
# => "foo"
Simone Carletti
  • 173,507
  • 49
  • 363
  • 364
2

The Ruby equivalent of your code looks like this:

class MyClass
  def initialize(variable)
    @variable = variable.freeze
  end
end

Generally this is frowned upon as this method doesn't have ownership of the object variable represents, so the caller might be in for a rude surprise when their object is suddenly frozen.

Note that this prevents manipulating the @variable object, but doesn't prevent repeated assignment to that property. There's nothing that can block that behaviour.

You can alway make a copy if applicable and freeze that.

In general practice you don't normally do this, but instead take a disciplined approach about not manipulating objects your class doesn't own, or making copies of them if you do need to make changes.

So, in summary, there's no tradition of locking things down like this in Ruby and very little support in the language for that sort of behaviour.

tadman
  • 208,517
  • 23
  • 234
  • 262
0

The closest thing might be a constant. A Ruby constant is like a variable, except that its value is supposed to remain constant for the duration of the program. The Ruby interpreter does not actually enforce the constancy of constants, but it does issue a warning if a program changes the value of a constant.

Joel
  • 4,503
  • 1
  • 27
  • 41