Imagine I want to write my own math operators like "+"
The simple version would be:
def plus(a,b)
return a+b
end
But this is not what the real "+" does.
I want 3.add(4) # =>7
But how do I tell ruby to take the object that I used my method on?
I tried
def add(c)
return self+c
end
But I get the error message:
:in <main>': private methodadd' called for 3:Fixnum (NoMethodError)
The problem
You defined the method:
def add(c)
return self + c
end
and attempted to use it thus:
3.add(4) #=> NoMethodError: private method `add' called for 3:Fixnum
Understanding this error message
This error message tells you exactly what the problem is. I think your problem is simply that you don't understand how Ruby invokes methods on objects.
When Ruby sees 3.add(4) it first looks at the receiver, 3, and determines:
3.class #=> Fixnum
This tells it where the method add is defined: in the class Fixnum or in one of Fixnum's ancestor's classes or modules.
So it looks for it there, doesn't find it, and issues an error message. We can confirm it's not there:
Fixnum.instance_methods.include?(:add)
#=> false
So where is add defined?
You did define it, though, so where is it? Let's find out:
method(:add).owner
#=> Object
Object.instance_methods.include?(:add)
#=> false
Object.instance_methods returns an array of all of public instance methods defined on Object and Object's ancestors. add is not among those, so we conclude add is a protected or private method:
Object.protected_instance_methods.include?(:add)
#=> false
Object.private_instance_methods.include?(:add)
#=> true
Let's try invoking that method on an instance of Object:
Object.new.add(4)
#=> NoMethodError:
# private method `add' called for #<Object:0x007fdb6a27fa68>
That makes sense, considering that Object#add is private. We can, however invoke private methods with Object#send:
Object.new.send(:add,4)
#NoMethodError: undefined method `+' for #<Object:0x007fdb6a28e068>
As an exercise, make sure you understand the steps Ruby took that led to her raising this exception (that the instance method + is not defined on Object, or equivalently, that the instance of Object does not have a method +).
By the way, where did you define add? By that, I mean what what was the value of self when you defined it? Let's see:
self #=> main
self.class #=> Object
We see that add must be defined on the class for which its receiver is an instance. (A mouthful, yes, but it's important, so make sure you understand that).
Why is Object#add private rather than public?
Consider:
def greet
puts 'hi'
end
class A
end
A.private_instance_methods.include?(:add)
#=> true
A.new.send(:greet)
#=> 'hi'
The is because A inherits greet from Object:
A.ancestors.include?(Object) #=> true
If Object#greet were public, every built-in class and every class you define would have a public instance method greet. That would result in a great deal of misery. (Suppose you had a method great and mistyped it greet!) Even the private greet could cause trouble.)
Where should add be defined?
Since add.class => Fixnum, we define it thus:
class Fixnum
def add(other)
self + other
end
end
Fixnum.instance_methods.include?(:add) #=> true
3.add(4) #=> 7
Had I included the line puts "self#{self}" after class Fixnum it would have printed "Fixnum". Salting your code with puts statements that show the value of self often helps in understanding what's going on.
One last thing:
method(:add).owner
#=> NameError: undefined method `add' for class `Object'
Why did this not return Fixnum? Since method has no explicit receiver (i.e., no xx.method), Ruby assumes the receiver to be self, which here is:
self #=> main
so she looks for the method method in self.class => Object, and you know what she finds (or, I should say, doesn't find). Instead, we need to write:
Fixnum.instance_method(:add).owner #=> Fixnum
or
3.method(:add).owner #=> Fixnum
Here 3 can of course be replaced by any instance of Fixnum.
Note I've simplified this explanation somewhat. In searching for a method, Ruby also looks in the receiver's singleton class. This is not an issue for immediate objects (numbers, symbols, true, false and nil), however, as they do not have singleton classes:
3.singleton_class #=> TypeError: can't define singleton
By contrast, for example:
[1,2].singleton_class #=> #<Class:#<Array:0x007fbcf18c01a8>>
The plus-sign (+) in ruby can be overridden pretty much like any other method (you can look for operator-overloading):
class MyOperator
attr_accessor :text
def initialize(text)
#text = text
end
def +(operand)
"#{self.text} #{operand.text}"
end
def to_s
self.text
end
end
a = MyOperator.new "Hello"
b = MyOperator.new "World"
puts (a+b)
So there is not much magic to it. But you have to be careful if the overloading the operators make sense in your context.
Related
Given this script
def hash
puts "why?"
end
x = {}
x[[1,2]] = 42
It outputs the following
why?
/tmp/a.rb:6:in `hash': no implicit conversion of nil into Integer (TypeError)
from /tmp/a.rb:6:in `<main>'
It seems that the hash function defned in the script is overriding Array#hash in that case. Since the return value of my hash method is nil and not an Integer, it throws an exception. The following script seems to confirm this
puts [1,2,3].hash
def hash
puts "why?"
end
puts [1,2,3].hash
The output is
-4165381473644269435
why?
/tmp/b.rb:6:in `hash': no implicit conversion of nil into Integer (TypeError)
from /tmp/b.rb:6:in `<main>'
I tried looking into the Ruby source code but could not figure out why this happens. Is this behavior documented?
You're not overriding Array#hash, you're shadowing Kernel#hash by creating Object#hash:
puts method(:hash)
def hash
puts "why?"
end
puts method(:hash)
That prints:
#<Method: Object(Kernel)#hash>
#<Method: Object#hash>
Fix it so we can see more:
def hash
puts "why?"
super
end
x = {}
x[[1,2]] = 42
Now the output is:
why?
why?
And no error. Try it with x[[1,2,3,4,5,6,7]] = 42 and you'll instead see why? printed seven times. Once for each array element, since the array's hash method uses the hashes of its elements. And Integer#hash doesn't exist, it inherits its hash method from Object/Kernel, so yours gets used.
This is due to a kind of hack in Ruby top level. Have you ever wondered how this works?
def foo
end
p self
foo
class Bar
def test
p self
foo
end
end
Bar.new.test # no error
How are two totally different objects (main and a Bar) able to call foo like it's a private method call? The reason is because... it is a private method call.
When you define a method at the top level of your Ruby script, it gets included (via Object) in every object. That's why you can call top-level methods like they are global functions.
But why does this break only hash and not other common methods? def to_s;end won't break to_s, for example. The reason is because hash is recursive: most* class implementations ultimately call down to Object#hash for their implementations. By redefining that base case, you break it globally. For other methods like to_s you won't see a global change because it's way up the inheritance chain and doesn't get invoked.
* the only objects this doesn't break are a few literals that probably have hard-coded hash values e.g. [] {} "" true etc.
Ruby's Class class lists two methods named 'new':
Class::new is a public class method
Class#new is a public instance method
But when I do:
Class.methods(false)
#=> []
which is supposed to list singleton methods (which I am assuming what class methods are), I get an empty array. Why is this so? Where is Class::new defined?
The methods shown as ::new in the documentation are usually #initialize, for example Range::new:
new(begin, end, exclude_end=false) → rng
Constructs a range using the given begin and end. If the exclude_end parameter is omitted or is false, the rng will include the end object; otherwise, it will be excluded.
This is because you create instances via:
r = Range.new(0, 2) #=> 0..2
Rather than:
r = Range.allocate #=> nil..nil
r.send(:initialize, 0, 2) #=> nil
r #=> 0..2
That's exactly what ::new does – it creates a new instance via allocate, sends it initialize (passing arguments) and returns the instance.
The actual new method is inherited from Class (since Range is an instance of Class) – Class#new:
new(args, ...) → obj
Calls allocate to create a new object of class’s class, then invokes that object’s initialize method, passing it args. This is the method that ends up getting called whenever an object is constructed using .new.
Just like allocate, inherited and superclass (and the instance methods from Class' ancestors like Module as well):
Range.method(:new)
#=> #<Method: Class#new>
Range.method(:allocate)
#=> #<Method: Class#allocate>
Range.method(:ancestors)
#=> #<Method: Class(Module)#ancestors>
So if you call Class.new:
my_string_class = Class.new(String)
#=> #<Class:0x007fdf5485b200>
you just invoke Class#new which is (again) equivalent to:
my_string_class = Class.allocate
my_string_class.send(:initialize, String)
my_string_class
#=> #<Class:0x007fdf5484beb8>
One notable exception is Struct which in fact provide its own new class method:
Struct.method(:new)
#=> #<Method: Struct.new>
Unlike other classes, Struct::new does not return instances of Struct but instances of Class (which are subclasses of Struct).
tl;dr summary:
Why is this so?
Because it's not a singleton method.
Where is Class::new defined?
It isn't. The call Class.new is just calling Class#new (since Class is an instance of itself). The documentation for Foo::new is actually the documentation for Foo#initialize for any class Foo, including Class itself.
If you want to know something about Ruby, it is often a good idea to ask her herself:
new_method = Class.method(:new)
#=> #<Method: Class#new (defined in Class at core/alpha.rb:90)>
The Object#method method returns a Method object representing the method. (Methods aren't objects in Ruby themselves, but you can get a reflective proxy object that represents a method.)
You can ask a Method where it is defined using the Method#owner method:
new_method.owner
#=> Class
As you can see, new is defined in Class and not in Class's singleton class.
You can also ask a Method about the location of its Ruby source code using the Method#source_location method:
new_method.source_location
#=> ['core/alpha.rb', 90]
What this tells us is that Class#new is defined in the file core/alpha.rb on line 90:
def new(*args)
obj = allocate()
Rubinius.asm(args, obj) do |args, obj|
run obj
run args
push_block
send_with_splat :initialize, 0, true
# no pop here, as .asm blocks imply a pop as they're not
# allowed to leak a stack value
end
obj
end
The method is partially implemented in bytecode for performance reasons, but it is basically just:
class Class
def new(*args, &block)
obj = allocate
obj.__send__(:initialize, *args, &block) # because initialize is private
#obj.initialize(*args, &block)
obj
end
end
Now, you might ask yourself: why is there an entry for Class::new in the RDoc documentation, if that method doesn't exist? Well, RDoc knows about the relationship between #initialize which is the method you define but usually don't call directly and Class#new which is the method you call but usually don't define, and it will document #initialize as ::new if it exists.
So, what we really want to look at, is Class#initialize:
initialize_method = Class.method(:initialize)
#=> #<Method: Class#initialize (defined in Class at core/class.rb:15)>
initialize_method.owner
#=> Class
initialize_method.source_location
#=> ['core/class.rb', 15]
This is the source:
def initialize(sclass=Object, name=nil, under=nil)
raise TypeError, "already initialized class" if #instance_type
raise TypeError, "can't make subclass of Class" if Class.equal?(sclass)
set_superclass sclass
# Things (rails) depend on the fact that a normal class is in the constant
# table and have a name BEFORE inherited is run.
under.const_set name, self if under
if sclass
Rubinius.privately do
sclass.inherited self
end
end
super()
end
private :initialize
Class#initialize essentially does three things:
set the superclass
optionally assign the class to a constant to give it a name
call the Class#inherited hook method of the superclass
If you want to know what the relationships between some of the core classes that magically spring into existence at the beginning are, you can take a look at the initialization code of some Ruby execution engines, e.g.
Rubinius: VM::bootstrap_class in machine/ontology.cpp
JRuby: org.jruby.Ruby.initRoot in core/src/main/java/org/jruby/Ruby.java
IronRuby: the initial classes are generated by a program, the generator is in the directory Src/ClassInitGenerator
MRuby: mrb_init_class in src/class.c
Note: depending on what Ruby implementation you use, obviously the places where those methods are defined and how exactly they are defined may vary.
new is defined as a instance method of Class class, rather than a singleton method:
Class.instance_method :new # => #<UnboundMethod: Class#new>
Tricky to note: Class (object) itself is also an instance of Class (class).
Class.instance_of? Class # => true
Is there a way to specify a class method such that when the object is used as if it were a function, that method is called? Something like this:
class MyClass
def some_magic_method(*args)
# stuff happens
end
end
# create object
myob = MyClass.new
# implicitly call some_magic_method
myob 'x'
You could write a command class and make use of a ruby shortcut
class MyClass
def self.call(text)
puts text
end
end
MyClass.('x')
Here MyClass.() defaults to the call class method.
As mentioned by #CarySwoveland in the comments you can use method_missing. A basic example is as follows:
class MyClass
def method_missing(method_name, *args)
if method_name.match?(/[xyz]/)
send(:magic_method, args.first)
else
super
end
end
def magic_method(a)
a = 'none' if a.nil?
"xyz-magic method; argument(s): #{a}"
end
end
myob = MyClass.new
myob.x #=> "xyz-magic method; argument(s): none"
myob.x(1) #=> "xyz-magic method; argument(s): 1"
myob.y #=> "xyz-magic method; argument(s): none"
myob.z #=> "xyz-magic method; argument(s): none"
This captures all methods named x, y or z. Our else branch sends all other undefined methods to the original method_missing:
myob.v #=> test.rb:7:in `method_missing': undefined method `v' for
#<MyClass:0x000000021914f8> (NoMethodError)
#from test.rb:25:in `<main>'
What methods you capture is up to you and is determined by the regex /[xyz]/ in this case.
Key methods: BasicObject#method_missing, Object#send. For further info check out this question, read Eloquent Ruby by Russ Olsen (from which this answer references)
You meant to invoke some class' instance method when the object is invoked as a function. This is already supported: instance method call gets called when you "invoke" an object via the functional invocation method () (for more details, see here How do I reference a function in Ruby?).
class C
def call(x)
puts "Called with #{x}"
end
end
obj = C.new
obj.(88) # Called with 88 => nil
obj (88) # NoMethodError: undefined method `obj' for main:Object
If you do want the latter syntax, a horrible trick is the following one (but works only at the top-level, unless you carry along the bindings):
module Kernel
def method_missing(name,*args)
obj = begin
TOPLEVEL_BINDING.local_variable_get(name)
rescue
nil
end
return super if obj.nil?
obj.send :call, *args
end
end
obj = C.new
obj 88 # Called with OK => nil
This example also wants to communicate that you should always keep in mind
who is the receiver of your method calls, and what syntaxes are available for calling methods (especially when you leave out dots and parentheses).
class D
def obj; C.new end
def f
#(obj) 88 # BAD
(obj).(88)
#obj() 88 # BAD
obj().(88)
end
end
The point is that you do not actually have functions, but methods that get called on objects. If you omit the receiver of a method call, the receiver defaults to self, the current object. But in your example, myob does not appear as an explicit receiver (since there is not following dot as in myob.), hence the current object is looked for a method myob.
I am reading the Module documentation but can't seem to understand their differences and which should be used where.
How is the eval different than exec?
I'm going to answer a bit more than your question by including instance_{eval|exec} in your question.
All variations of {instance|module|class}_{eval|exec} change the current context, i.e. the value for self:
class Array
p self # prints "Array"
43.instance_eval{ p self } # prints "43"
end
Now for the differences. The eval versions accepts a string or a block, while the exec versions only accept a block but allow you to pass parameters to it:
def example(&block)
42.instance_exec("Hello", &block)
end
example{|mess| p mess, self } # Prints "Hello" then "42"
The eval version does not allow to pass parameters. It provides self as the first parameter, although I can't think of a use for this.
Finally, module_{eval|exec} is the same as the corresponding class_{eval|exec}, but they are slightly different from instance_{eval|exec} as they change what is the current opened class (i.e. what will be affected by def) in different ways:
String.instance_eval{ def foo; end }
Integer.class_eval { def bar; end }
String.method_defined?(:foo) # => false
String.singleton_methods.include?(:foo) # => true
Integer.method_defined?(:bar) # => true
So obj.instance_{eval|exec} opens the singleton class of obj, while mod.{class|module}_{eval|exec} opens mod itself.
Of course, instance_{eval|exec} are available on any Ruby object (including modules), while {class|module}_* are only available on Module (and thus Classes)
To answer your last question first, eval (in all its variations) is completely different from exec. exec $command will start a new process to run the command you specify and then exit when that finishes.
class_eval and module_eval have the power to redefine classes and modules -- even those that you yourself did not write. For example, you might use class eval to add a new method that did not exist.
Fixnum.class_eval { def number; self; end }
7.number # returns '7'
class_eval can be used to add instance methods, and instance_eval can be used to add class methods (yes, that part is very confusing). A class method would be something like Thing.foo -- you're literally calling the foo method on the Thing class. An instance method is like the example above, using class_eval I've added a number method to every instance of Fixnum.
Okay, so that's the *_eval class of methods. The exec methods are similar, but they allow you to look inside a class and execute a block of code as though it was defined as a method on that class. Perhaps you have a class that looks like this:
class Foo
##secret = 'secret key'
##protected = 'some secret value'
def protected(key)
if key == ##secret
return ##protected
end
end
end
The class Foo is just a wrapper around some secret value, if you know the correct key. However, you could trick the class into giving you its secrets by executing a block inside the context of the class like so:
Foo.class_exec { ##secret = 'i'm a hacker' }
Foo.protected('i'm a hacker') #returns the value of ##protected because we overwrote ##secret
In general, with a lot of the tools in ruby, you could use any of these to solve a lot of problems. A lot of the time you probably won't even need to unless you want to monkey patch a class some library you use has defined (although that opens up a whole can of worms). Try playing around with them in irb and see which you find easier. I personally don't use the *_exec methods as much as the *_eval methods, but that's a personal preference of mine.
To avoid ambiguity I'm going to call a method that belongs to (owned by) a singleton class a singleton method. The rest are instance methods. Although one might say that a singleton method of an object is an instance method of its singleton class.
tl;dr Use class_eval/module_eval on a class/module to define instance methods, and instance_eval on a class/module to define class methods (or to be more precise, use instance_eval to define singleton methods). Additionally you can use instance_eval to access instance variables.
A terminology is a bit lacking in this case. ruby maintains a stack of class references (cref for short). When you open/reopen a class, the corresponding class reference is pushed to the stack. And the current class refernece affects where def defines methods (to which class/module they're added).
Now, class_eval/module_eval and class_exec/module_exec are aliases.
The *_exec() variants don't accept strings, and allow to pass arguments to the block. Since the *_eval() variants are mainly used I'll focus on them.
class_eval/module_eval changes cref and self to the receiver (Thing in Thing.module_eval(...)):
rb_mod_module_eval() -> specific_eval()
yield_under() (for blocks)
vm_cref_push()
eval_under() (for strings)
vm_cref_push()
instance_eval changes cref to the singleton class of the receiver, and self to the receiver.
Let's see them in action:
class A
p self #=> A
#a = 1
def initialize
#b = 2
end
end
p A.instance_variables #=> [:#a]
p A.new.instance_variables #=> [:#b]
#a on a class level adds an instance variable to the class A as an object. I'm adding it here for completeness. But that's not how you add a class variable.
A.instance_eval do
p self #=> A
p #a #=> 1
def m() puts 'm' end
end
sclass = A.singleton_class
p sclass.instance_methods(false).include? :m #=> true
A.m #=> m
a = A.new
a.instance_eval do
p self #=> #<A:0x00007fc497661be8 #b=2>
p #b #=> 2
def m2() puts 'm2' end
end
sclass = a.singleton_class
p sclass.instance_methods(false).include? :m2 #=> true
a.m2 #=> m2
So, inside instance_eval def adds a singleton method to the receiver (an instance method to the singleton class of the receiver). For a class/module that means a class/module method. For other objects, a method that is available for that particular object.
A.class_eval do
p self #=> A
p #a #=> 1
def m() puts 'm' end
end
p A.instance_methods(false).include? :m #=> true
A.new.m #=> m
And, inside class_eval def adds an instance method to the receiver itself (the class/module). class_eval is only available for classes/modules.
Also, when class_eval is passed a block, constant/class variable lookup is not affected:
module A
C = 1
##c = 1
class B
C = 2
##c = 2
end
A::B.class_eval { p [C, ##c] } #=> [1, 1]
A::B.class_eval 'p [C, ##c]' #=> [2, 2]
end
The naming is confusing. I might guess that instance in instance_eval suggests that receiver is treated as an instance (allows to change things for a particular instance), and class in class_eval as a class (allows to change things for a class of objects).
class MyClass
def mymethod
MYCONSTANT = "blah"
end
end
gives me the error:
SyntaxError: dynamic constant assignment error
Why is this considered a dynamic constant? I'm just assigning a string to it.
Your problem is that each time you run the method you are assigning a new value to the constant. This is not allowed, as it makes the constant non-constant; even though the contents of the string are the same (for the moment, anyhow), the actual string object itself is different each time the method is called. For example:
def foo
p "bar".object_id
end
foo #=> 15779172
foo #=> 15779112
Perhaps if you explained your use case—why you want to change the value of a constant in a method—we could help you with a better implementation.
Perhaps you'd rather have an instance variable on the class?
class MyClass
class << self
attr_accessor :my_constant
end
def my_method
self.class.my_constant = "blah"
end
end
p MyClass.my_constant #=> nil
MyClass.new.my_method
p MyClass.my_constant #=> "blah"
If you really want to change the value of a constant in a method, and your constant is a String or an Array, you can 'cheat' and use the #replace method to cause the object to take on a new value without actually changing the object:
class MyClass
BAR = "blah"
def cheat(new_bar)
BAR.replace new_bar
end
end
p MyClass::BAR #=> "blah"
MyClass.new.cheat "whee"
p MyClass::BAR #=> "whee"
Because constants in Ruby aren't meant to be changed, Ruby discourages you from assigning to them in parts of code which might get executed more than once, such as inside methods.
Under normal circumstances, you should define the constant inside the class itself:
class MyClass
MY_CONSTANT = "foo"
end
MyClass::MY_CONSTANT #=> "foo"
If for some reason though you really do need to define a constant inside a method (perhaps for some type of metaprogramming), you can use const_set:
class MyClass
def my_method
self.class.const_set(:MY_CONSTANT, "foo")
end
end
MyClass::MY_CONSTANT
#=> NameError: uninitialized constant MyClass::MY_CONSTANT
MyClass.new.my_method
MyClass::MY_CONSTANT #=> "foo"
Again though, const_set isn't something you should really have to resort to under normal circumstances. If you're not sure whether you really want to be assigning to constants this way, you may want to consider one of the following alternatives:
Class variables
Class variables behave like constants in many ways. They are properties on a class, and they are accessible in subclasses of the class they are defined on.
The difference is that class variables are meant to be modifiable, and can therefore be assigned to inside methods with no issue.
class MyClass
def self.my_class_variable
##my_class_variable
end
def my_method
##my_class_variable = "foo"
end
end
class SubClass < MyClass
end
MyClass.my_class_variable
#=> NameError: uninitialized class variable ##my_class_variable in MyClass
SubClass.my_class_variable
#=> NameError: uninitialized class variable ##my_class_variable in MyClass
MyClass.new.my_method
MyClass.my_class_variable #=> "foo"
SubClass.my_class_variable #=> "foo"
Class attributes
Class attributes are a sort of "instance variable on a class". They behave a bit like class variables, except that their values are not shared with subclasses.
class MyClass
class << self
attr_accessor :my_class_attribute
end
def my_method
self.class.my_class_attribute = "blah"
end
end
class SubClass < MyClass
end
MyClass.my_class_attribute #=> nil
SubClass.my_class_attribute #=> nil
MyClass.new.my_method
MyClass.my_class_attribute #=> "blah"
SubClass.my_class_attribute #=> nil
SubClass.new.my_method
SubClass.my_class_attribute #=> "blah"
Instance variables
And just for completeness I should probably mention: if you need to assign a value which can only be determined after your class has been instantiated, there's a good chance you might actually be looking for a plain old instance variable.
class MyClass
attr_accessor :instance_variable
def my_method
#instance_variable = "blah"
end
end
my_object = MyClass.new
my_object.instance_variable #=> nil
my_object.my_method
my_object.instance_variable #=> "blah"
MyClass.new.instance_variable #=> nil
In Ruby, any variable whose name starts with a capital letter is a constant and you can only assign to it once. Choose one of these alternatives:
class MyClass
MYCONSTANT = "blah"
def mymethod
MYCONSTANT
end
end
class MyClass
def mymethod
my_constant = "blah"
end
end
Constants in ruby cannot be defined inside methods. See the notes at the bottom of this page, for example
You can't name a variable with capital letters or Ruby will asume its a constant and will want it to keep it's value constant, in which case changing it's value would be an error an "dynamic constant assignment error". With lower case should be fine
class MyClass
def mymethod
myconstant = "blah"
end
end
Ruby doesn't like that you are assigning the constant inside of a method because it risks re-assignment. Several SO answers before me give the alternative of assigning it outside of a method--but in the class, which is a better place to assign it.
Many thanks to Dorian and Phrogz for reminding me about the array (and hash) method #replace, which can "replace the contents of an array or hash."
The notion that a CONSTANT's value can be changed, but with an annoying warning, is one of Ruby's few conceptual mis-steps -- these should either be fully immutable, or dump the constant idea altogether. From a coder's perspective, a constant is declarative and intentional, a signal to other that "this value is truly unchangeable once declared/assigned."
But sometimes an "obvious declaration" actually forecloses other, future useful opportunities. For example...
There are legitimate use cases where a "constant's" value might really need to be changed: for example, re-loading ARGV from a REPL-like prompt-loop, then rerunning ARGV thru more (subsequent) OptionParser.parse! calls -- voila! Gives "command line args" a whole new dynamic utility.
The practical problem is either with the presumptive assumption that "ARGV must be a constant", or in optparse's own initialize method, which hard-codes the assignment of ARGV to the instance var #default_argv for subsequent processing -- that array (ARGV) really should be a parameter, encouraging re-parse and re-use, where appropriate. Proper parameterization, with an appropriate default (say, ARGV) would avoid the need to ever change the "constant" ARGV. Just some 2¢-worth of thoughts...