What is the point of making an argument an array by default? - ruby

Sometimes, when a method needs to be given an array as an argument, I see the method defined like this:
def method(argument = [])
...
end
I don't understand why = [] is used. As far as I can see, it adds nothing. If you did supply an array as an argument, the method would run either way, and if you didn't, it would throw an error either way. Is it just convention? Or is it perhaps a visual aid so the programmer can easily see what type of data a method requires?

If you set default argument here, it won't raise an error if you call this method without arguments:
method
# => []

Specifying a default value allows you to call the method with AND without that param.
I have found it helpful while adding a new variable to an existing method. If I specify a default value to the new variable, I don't have to worry about changing the previous calls to that method which have only the previous set of variables. If, however, I am not able to specify a default value for the new variable, I will have to go through the code and hunt down all the method calls, and modify it to include the new variable.

Related

Is there a way to store an instance method inside a variable without running that function first? In Ruby

I am trying to store an instance method as a variable so I can pass it into a way to store logic on a menu I am building.
For example, I want my our_startup.prompt method to be stored in my start_game_ui.logic array. I am trying to do this using a start_game_ui.set_logic function which shovels arguments into the logic array. I would like to shovel 6 methods into the logic array so that when I run my final function to puts and receive input 1 - 6. If the user chooses 1 it should run the function in the first element of the array.
our_startup = Startup.new("No Name")
## START GAME UI ##
start_game_ui = UI.new("start_game_ui")
start_game_ui.menu_items = ["[1] - Yes", "[2] - Generate Another"]
##set up logic##
method1_test = our_startup.set_name("test")
rerun = start_game_ui.prompt
start_game_ui.set_logic(method1_test, rerun)
When I run this, my start_game_ui.prompt method will run. I want to store the start_game_ui.prompt method in that variable rerun without having the method run.
Once I run my final method and choose 1 it should return "test". However when I run this it runs start_game_ui.prompt and I don't want it to.
I hope you can understand what I mean. I have 2 classes UI and Startup if you couldn't already tell.
PLEASE DO NOT TELL ME I CAN DO method(:something) this does not help as it is an instance method being called by another instance. Unless you can tell me how to get that symbol to correspond with the correct method inside the instance. I've tried method(our_startup.prompt) and it does not work.
PLEASE DO NOT TELL ME I CAN DO method(:something) this does not help as it is an instance method being called by another instance.
I would really like to not tell you that, but unfortunately, that is the correct answer:
rerun = start_game_ui.method(:prompt)
# Then, later when you need to call it:
result = rerun.()
Not using Object#method, as you require in your question, leads to significant added complexity, e.g. by passing around the receiver and the name of the method separately:
rerun_receiver_and_message = [start_game_ui, :prompt]
# Then, later when you need to call it:
result = rerun_receiver_and_message.first.public_send(rerun_receiver_and_message.last)
The only thing you can store in variables and pass as arguments are objects.
Procs and Lambdas are objects, so you should be able to do something like
rerun = -> {start_game_ui.prompt}
start_game_ui.set_logic(method1_test, rerun)
rerun is storing the call to the method, not the results of the method
At the point where you need to execute the method, you would do
rerun.call
Note that Procs and Lambdas can also specify arguments, that you can supply at the time of the call.
I'm not sure if this helps with your problem, but hope it does.
If you'd like to get the instance method from inside the instance of your object, then you can use this: our_startup.method(:prompt)
I don't really understand what your end goal is, so I'm going to suggest you read up a little further on Ruby's object model and methods and provide you some guidance instead.
The method method returns an instance of Method (an object). If this is confusing, read it more slowly and check out the Method documentation. Whether the method being referenced by the argument is an instance method or not is irrelevant to the behavior of the method method.
Directly addressing something you said in the question: using method(:foo) does not call the referenced method (e.g. foo).
You can unbind a method from the source receiver (creating an UnboundMethod that can't be called) and rebind it to another receiver if you need to:
my_method_instance = some_string.method(:to_s)
# Now I can call `some_string.to_s` like so:
my_method_instance.to_s
# This isn't very useful for `to_s`, but it could be in other situations
method_instance = SomeModule::SomeHelper.method(:parse_html)
array_of_html_docs = array_of_strings.map(&method_instance)
# And you can unbind the method from the original receiver:
my_unbound_method_instance = my_method_instance.unbind
# And rebind it elsewhere
my_unbound_method_instance.bind(some_other_receiver)
my_unbound_method_instance.call(args) # receiver is `some_other_receiver` here

How does Ruby handle assignment semantically?

In Ruby, we assign values to objects with the = operator.
Combine this with implicit typing and we frequently get situations like this:
myVar= :asymbol
The above line both creates a new symbol object, and binds the object to the variable name myVar.
Semantically, how is this done?
I have had it hammered into my head that the = operator is not magic syntax built into the interpreter, but is actually just syntactic sugar for the object.=(value) method.
With this in mind, my best guess is that when the interpreter sees we are trying to assign a value to an undefined variable name, it first creates a new object of some special type, like undefined or null or something, and then passes the := message to that object with the payload being the value we are trying to assign.
However, calling .class on an un-instantiated object just throws an exception because Ruby thinks we're trying to call a method (whose name is the name of the variable that you're trying to bring into existence) on self
> obj.class
> NameError: undefined variable or method 'obj' for main:Object
So, as far as I can tell, I have no way of figuring this out experimentally.
Side note:
In the case of symbol assignment, I believe that the value assigned ( A.K.A. the value returned by the instantiated object's object_id method, A.K.A. the value of the unsigned long VALUE variable on the C level) is a number that represents an offset in a table somewhere (I believe this is how Ruby achieves 'immediate value' for symbol objects).
In other cases, the value may be a direct encoding of the object itself, or a value that is meant to be cast to a pointer in reference to a struct.
Regardless, the way that Ruby represents the object and whether we end up assigning a reference or the object itself is not what I am asking about here.
Additional question:
What class is the = method inherited from? I can't find it in the spec for Object or BasicObject.
Variables are, in a technical sense, just pointers to objects. There's nothing remarkable about that, but a simple variable assignment to an existing object does not involve any method calls or messages being sent.
Remember variables are just there so that programmers can refer to objects by name instead of by some kind of internal identifier or memory location. So there's a bit of "magic" here, = is special when making an assignment as there's rules for what you can do on the left and right side of it.
The only way you can send messages to something, that is make method calls, is if you've defined it in a way the compiler understands. x = 1 is sufficient, it means x refers to the Fixnum in question.
Note that the Ruby interpreter will need to determine if x refers to a variable or method call, as x= may be a method that's defined on the object context in which this is evaluated.
For example:
class Example
def x=(value)
#x = value
end
def test
# Equivalent to send(:x=, 1) because x= is a method
x = 1
# Is a variable definition because y= is not a method
y = 2
# Is always a method call because self is referenced.
self.x = 3
end
end
# Is a variable definition because x= is not defined in this context
x = 4
If there's no x= method for your object, x is automatically presumed to be a variable.
You can't have a := message because that would imply you can replace one object with another, something that's not allowed. Once an object is created, it cannot magically change type. For that you need to create a new instance of a different object. Variables only appear to change types, but in fact, they just end up pointing to different objects.
So in short, there's no := method call, but there may be special methods like :x= that work in very specific cases.

Calling original new() method from within overridden new()?

I have a class whose initialize method gets data from a remote source, which it then uses to set its object attributes.
I'm expecting this class to be heavily used, possibly hundreds of times within a program. I'd like to reduce the overhead network calls by caching objects that have already been instantiated, which I'd then return to the user when they ask to instantiate that object again.
For that, I've been considering overriding the new method for this class. It would check to see if the cached object is available, and if so, return that reference. Otherwise, it would call the regular new method for the object, which would allocate memory and call initialize like usual.
If I override the new() method in a class, is it possible to call the original new method via something like super()?
Yes, super without parameters will call the parent method with the same parameters passed to the new method.
Or, you can cherry-pick parameters by adding them to super(p1, p2, ...).
Regarding what you want to do by remembering previous invocations, that's called "memoizing" and there is at least one memoize gem for it, or, you can write your own, depending on your needs.
It's pretty easy to do using a hash. Use the parameters used when invoking the new method as the key, and the value is the instance you want to return. Without examples of your code it's hard to come up with an example that's custom-fit, but this is a simple, untested, version:
def initialize(*args)
#memoizer ||= {}
return #memoizer[args] if #memoizer[args]
# do what you will with the args in this initializer,
# then create a new instance for the future.
#memoizer[args] = super(args)
end
The idea is that #memoizer remembers the "arity" of the call and automatically returns the result of similar calls. If that set of parameters haven't been seen before it'll compute and create the new instance and then return it.
This breaks down when the result could change with the same set of input parameters. You don't want to memoize database calls or anything using random or a date/time value, or that returns something outside your control. Trying to use it in those cases will return stale or wrong values unless you design in a method to sweep through and revalidate the #memoizer values periodically.
Also, there is no flushing mechanism, so #memoizer will only grow in size, and could possibly consume all available space given enough different input values. To deal with that you could also have a timestamp for when the value was added to #memoizer, and periodically purge entries that exceed a given lifetime. Only "live" values would remain in the hash then.
Useful information is at: "Memoize Techniques in Ruby and Rails".
either via super or via an alias_method chain
super is better if you sub-class
Check this: When monkey patching a method, can you call the overridden method from the new implementation?

Ruby module variable accessor not working as expected

So I want a module with a variable and access methods.
My code looks something like this
module Certificates
module Defaults
class << self
attr_accessor :address
def get_defaults
address = "something"
make_root_cert
end
def make_root_cert
blub = address
# do somthing
end
end
end
I inspected it with pry.
The result is
Certificates::Defaults has methods called address and address=.
If I call address in the get_defaults method it returns "something" as expected
If I call it in make_root_cert it returns nil
I used this way of attr_accessor creation in another module and it worked fine. I hope I'm just misunderstanding the way ruby works and somebody can explain why this example doesn't work. Maybe using the implementation details of the ruby object model.
Jeremy is right.
My findings
This seems inconsistent to me.
If you use the expression "address" and the instance variable has not been set it returns the local variable
If the instance variable has been set and the local variable not it returns the instance variable.
If both have been set it returns the local variable.
On the other hand address="test" always sets the local variable.
In your get_defaults methods, address is a local variable. To use the setter, you have to type this:
self.address = "something"
That will properly call the address= method.
This rather confusing behavior occurs because the Ruby interpreter places local variable definition at a higher precedence than method calls. There is consistency here, but unless you know how it works in advance it can be hard to see clearly.
Given that so many things in Ruby are objects and method calls, it might be natural to assume that variable definition was some sort of a method called on something (like Kernel or main or the object in which it was defined or whatever) and that the resulting variable was some sort of object. If this was the case, you would guess that the interpreter would resolve name conflicts between variable definitions and other methods according to the rules of method lookup, and would only define a new variable if it didn't find a method with the same name as the potential variable definition first.
However, variable definition is not a method call, and variables are not objects. Instead, variables are just references to objects, and variable definition is something the interpreter keeps track of below the surface of the language. This is why Kernel.local_variables returns an array of symbols, and there's no way to get an array of some sort of local variable objects.
So, Ruby needs a special set of rules to handle name conflicts between variables and methods. Non-local variables have a special prefix denoting their scope ($, #, etc.) which fixes this, but not so for local variables. If Ruby required parens after methods, that would also address this problem, but we are given the luxury of not having to do that. To get the convenience of referencing local variables without a prefix and invoking methods without parens, the language just defaults to assuming you want the local variable whenever it's in scope. It could have been designed the other way, but then you would have weird situations where you defined a local variable and it was instantly eclipsed by some faraway method with the same name halfway across the program, so it's probably better like this.
The Ruby Programming Language, p. 88, has this to say:
"...local variables don't have a punctuation character as a prefix. This means that local variable references look just like method invocation expressions. If the Ruby interpreter
has seen an assignment to a local variable, it knows it is a variable and not a method, and it can return the value of the variable. If there has been no assignment, then Ruby treats the expression as a method invocation. If no method by that name exists, Ruby raises a NameError."
It goes on to explain why you were getting nil when calling address in make_root_cert:
"In general, therefor, attempting to use a local variable before it has been initialized results in an error. There is one quirk--a variable comes into existence when the Ruby interpreter sees an assignment expression for that variable. This is the case even if that assignment is not actually executed. A variable that exists but has not been assigned a value is given the default value nil. For example:
a = 0.0 if false # This assignment is never executed
print a # Prints nil: the variable exists but is not assigned
print b # NameError: no variable or method named b exists"
The setter method you get with attr_accessor leads the interpreter to create a variable before the setter method is ever called, but it has to be called to assign that variable a value other than nil. address = "something" in get_defaults defines a local variable within that method called address that goes out of scope at the end of the method. When you call make_root_cert, there's no local variable called address, so the getter method address that you got with attr_accessor is called and returns nil because the setter method hasn't been called to give it some other value. self.address= lets the interpreter know that you want the class method address= instead of a new local variable, resolving the ambiguity.

Which is better? Creating a instance variable or passing around a local variable in Ruby?

In general what is the best practice and pro/cons to creating an instance variable that can be accessed from multiple methods or creating an instance variable that is simply passed as an argument to those methods. Functionally they are equivalent since the methods are still able to do the work using the variable. While I could see a benefit if you were updating the variable and wanted to return the updated value but in my specific case the variable is never updated only read by each method to decide how to operate.
Example code to be clear:
class Test
#foo = "something"
def self.a
if #foo == "something"
puts "do #{#foo}"
end
end
a()
end
vs
class Test
foo = "something"
def self.a(foo)
if foo == "something"
puts "do #{foo}"
end
end
a(foo)
end
I don't pass instance variable around. They are state values for the instance.
Think of them as part of the DNA of that particular object, so they'll always be part of what makes the object be what it is. If I call a method of that object, it will already know how to access its own DNA and will do it internally, not through some parameter being passed in.
If I want to apply something that is foreign to the object, then I'll have to pass it in via the parameters.
As you mentioned, this is a non-functional issue about the code. With that in mind...
It's hard to give a definitive rule about it since it depends entirely on the context. Is the variable set once and forgotten about it, or constantly updated? How many methods share the same variable? How will the code be used?
In my experience, variables that drive behavior of the object but are seldom (if at all) modified are set in the initialize method, or given to the method that will cascade behavior. Libraries and leaf methods tend to have the variable passed in, as it's likely somebody will want to call it in isolation.
I'd suggest you start by passing everything first, and then refactoring if you notice the same variable being passed around all over the class.
If I need a variable that is scoped at the instance level, I use an instance variable, set in the initialize method.
If I need a variable that is scoped at the method level (that is, a value that is passed from one method to another method) I create the variable at the method level.
So the answer to your question is "When should my variable be in scope" and I can't really answer that without seeing all of your code and knowing what you plan to do with it.
If your object behavior should be statically set in the initialization phase, I would use an instance variable.

Resources