Ruby - writing a method without a "parameter" - ruby

For example, the array class has a method named ".sort" that you call on an array with the following syntax:
[2, 3, 4, 1].sort
From my limited knowledge, I only know how to write methods that take an actual parameter:
def sort(array)
...
end
and are called as such:
sort([2, 3, 4, 1])
How would you go about setting up a method that can be called with the dot notation (please correct me if I'm wrong) syntax?
*To clarify, I'm trying to add a method into the existing class Array that can remove duplicate objects. I know the ".uniq" method already does that, but I'm re-writing it for the purpose of learning how its done. I only know how to write the method as such:
def my_uniq(array)
...
end
where the array is the parameter that I can work with inside my method.

You should start thinking in terms of sending messages to object. i.e. Instead of seeing them as calling methods, see them as sending message to object(s). When you do send messages, the object would check if it can fulfill the request (ie. check if there is a method by that name exists in here or in its parent) if so it does. So in essence, the sort is a message (method) to array object.
class Array
def sort
# do sorting
end
# example taken from #codycaughlan
# allows you to send message `hi` to your array object
def hi
"Hi, #{length}"
end
end
# so instead of calling method hi([2,9,0])
# send hi message to an array object
[2,9,0].hi
# instead of calling method sort([2,9,0])
# send sort message to an array object
[2,9,0].sort
Update
Let us say you want to write a method that works on an array that prints * multiplied by the number you pass.
class Array
def print_stars
self.each { |i| puts "*" * i}
end
end
# now let us send a message / call
[1,2,3].print_stars #=> this should produce
*
**
***
As you may already know, these messages can be chained. For e.g.
[1,2,3].reverse.print_stars #=> produces
***
**
*

It depends on what the "argument" is. In your example of:
[2, 3, 4, 1].sort
The argument is an instance of Array. You could monkey-patch Array to add a method of your choosing:
class Array
def hi
"Hi, #{length}"
end
end
puts [1,2,3].hi
=> Hi, 3

Related

Overriding the << method for instance variables

Let's suppose I have this class:
class Example
attr_accessor :numbers
def initialize(numbers = [])
#numbers = numbers
end
private
def validate!(number)
number >= 0 || raise(ArgumentError)
end
end
I would like to run the #validate! on any new number before pushing it into the numbers:
example = Example.new([1, 2, 3])
example.numbers # [1, 2, 3]
example.numbers << 4
example.numbers # [1, 2, 3, 4]
example.numbers << -1 # raise ArgumentError
Below is the best I can do but I'm really not sure about it.
Plus it works only on <<, not on push. I could add it but there is risk of infinite loop...).
Is there a more "regular" way to do it? I couldn't find any official process for that.
class Example
attr_accessor :numbers
def initialize(numbers = [])
#numbers = numbers
bind = self # so the instance is usable inside the singleton block
#numbers.singleton_class.send(:define_method, :<<) do |value|
# here, self refers to the #numbers array, so use bind to refer to the instance
bind.send(:validate!, value)
push(value)
end
end
private
def validate!(number)
number >= 0 || raise(ArgumentError)
end
end
Programming is a lot like real life: it is not a good idea to just run around and let strangers touch your private parts.
You are solving the wrong problem. You are trying to regulate what strangers can do when they play with your private parts, but instead you simply shouldn't let them touch your privates in the first place.
class Example
def initialize(numbers = [])
#numbers = numbers.clone
end
def numbers
#numbers.clone.freeze
end
def <<(number)
validate(number)
#numbers << number
self
end
private
def validate(number)
raise ArgumentError, "number must be non-negative, but is #{number}" unless number >= 0
end
end
example = Example.new([1, 2, 3])
example.numbers # [1, 2, 3]
example << 4
example.numbers # [1, 2, 3, 4]
example << -1 # raise ArgumentError
Let's look at all the changes I made one-by-one.
cloneing the initializer argument
You are taking a mutable object (an array) from an untrusted source (the caller). You should make sure that the caller cannot do anything "sneaky". In your first code, I can do this:
ary = [1, 2, 3]
example = Example.new(ary)
ary << -1
Since you simply took my array I handed you, I can still do to the array anything I want!
And even in the hardened version, I can do this:
ary = [1, 2, 3]
example = Example.new(ary)
class << ary
remove_method :<<
end
ary << -1
Or, I can freeze the array before I hand it to you, which makes it impossible to add a singleton method to it.
Even without the safety aspects, you should still do this, because you violate another real-life rule: Don't play with other people's toys! I am handing you my array, and then you mutate it. In the real world, that would be considered rude. In programming, it is surprising, and surprises breed bugs.
cloneing in the getter
This goes to the heart of the matter: the #numbers array is my private internal state. I should never hand that to strangers. If you don't hand the #numbers array out, then none of the problems you are protecting against can even occur.
You are trying to protect against strangers mutating your internal state, and the solution to that is simple: don't give strangers your internal state!
The freeze is technically not necessary, but I like it to make clear to the caller that this is just a view into the state of the example object, and they are only allowed to view what I want them to.
And again, even without the safety aspects, this would still be a bad idea: by exposing your internal implementation to clients, you can no longer change the internal implementation without breaking clients. If you change the array to a linked list, your clients are going to break, because they are used to getting an array that you can randomly index, but you can't randomly index a linked list, you always have to traverse it from the front.
The example is unfortunately too small and simple to judge that, but I would even question why you are handing out arrays in the first place. What do the clients want to do with those numbers? Maybe it is enough for them to just iterate over them, in which case you don't need to give them a whole array, just an iterator:
class Example
def each(...)
return enum_for(__callee__) unless block_given?
#numbers.each(...)
self
end
end
If the caller wants an array, they can still easily get one by calling to_a on the Enumerator.
Note that I return self. This has two reasons:
It is simply the contract of each. Every other object in Ruby that implements each returns self. If this were Java, this would be part of the Iterable interface.
I would actually accidentally leak the internal state that I work so hard to protect! As I just wrote: every implementation of each returns self, so what does #numbers.each return? It returns #numbers, which means my whole Example#each method returns #numbers which is exactly the thing I am trying to hide!
Implement << myself
Instead of handing out my internal state and have the caller append to it, I control what happens with my internal state. I implement my own version of << in which I can check for whatever I want and make sure no invariants of my object are violated.
Note that I return self. This has two reasons:
It is simply the contract of <<. Every other object in Ruby that implements << returns self. If this were Java, this would be part of the Appendable interface.
I would actually accidentally leak the internal state that I work so hard to protect! As I just wrote: every implementation of << returns self, so what does #numbers << number return? It returns #numbers, which means my whole Example#<< method returns #numbers which is exactly the thing I am trying to hide!
Drop the bang
In Ruby, method names that end with a bang mean "This method is more surprising than its non-bang counterpart". In your case, there is no non-bang counterpart, so the method shouldn't have a bang.
Don't abuse boolean operators for control flow
… or at least if you do, use the keyword versions (and / or) instead of the symbolic ones (&& / ||).
But really, you should void it altogether. do or die is idiomatic in Perl, but not in Ruby.
Technically, I have changed the return value of your method: it used to return true for a valid value, now it returns nil. But you ignore its return value anyway, so it doesn't matter.
validate is probably not a good name for the method, though. I would expect a method named validate to return a boolean result, not raise an exception.
An exceptional message
You should add messages to your exceptions that tell the programmer what went wrong. Another possibility is to create more specific exceptions, e.g.
class NegativeNumberError < ArgumentError; end
But that would be overkill in this case. In general, if you expect code to "read" your exception, create a new class, if you expect humans to read your exception, then a message is enough.
Encapsulation, Data Abstraction, Information Hiding
Those are three subtly different but related concepts, and they are among the most important concepts in programming. We always want hide our internal state and encapsulate it behind methods that we control.
Encapsulation to the max
Some people (including myself) don't particularly like even the object itself playing with its internal state. Personally, I even encapsulate private instance variables that are never exposed behind getters and setters. The reason is that this makes the class easier to subclass: you can override and specialize methods, but not instance variables. So, if I use the instance variable directly, a subclass cannot "hook" into those accesses.
Whereas if I use getter and setter methods, the subclass can override those (or only one of those).
Note: the example is too small and simple, so I had some real trouble coming up with a good name (there is not enough in the example to understand how the variable is used and what it means), so eventually, I just gave up, but you will see what I mean about using getters and setters:
class Example
class NegativeNumberError < ArgumentError; end
def initialize(numbers = [])
self.numbers_backing = numbers.clone
end
def each(...)
return enum_for(__callee__) unless block_given?
numbers_backing.each(...)
self
end
def <<(number)
validate(number)
numbers_backing << number
self
end
private
attr_accessor :numbers_backing
def validate(number)
raise NegativeNumberError unless number >= 0
end
end
example = Example.new([1, 2, 3])
example.each.to_a # [1, 2, 3]
example << 4
example.each.to_a # [1, 2, 3, 4]
example << -1 # raise NegativeNumberError

can somebody explain how does the following code execute?

I am following a linked tutorial from the Odin project, its about blocks and procs in ruby. I can't quite understand how does the following code work.
class Array
def eachEven(&wasABlock_nowAProc)
# We start with "true" because arrays start with 0, which is even.
isEven = true
self.each do |object|
if isEven
wasABlock_nowAProc.call object
end
isEven = (not isEven) # Toggle from even to odd, or odd to even.
end
end
end
['apple', 'bad apple', 'cherry', 'durian'].eachEven do |fruit|
puts 'Yum! I just love '+fruit+' pies, don\'t you?'
end
# Remember, we are getting the even-numbered elements
# of the array, all of which happen to be odd numbers,
# just because I like to cause problems like that.
[1, 2, 3, 4, 5].eachEven do |oddBall|
puts oddBall.to_s+' is NOT an even number!'
end
Is ['apple', 'bad apple', 'cherry', 'durian'] a block in this context and are we calling the method isEven on that block?
Does isEven used to only return true or false and if true the following code will be executed?
do |fruit|
puts 'Yum! I just love '+fruit+' pies, don\'t you?'
end
Also, what is this line doing?
self.each do |object|
if isEven
wasABlock_nowAProc.call object
end
end
If isEven is true then call [1, 2, 3, 4, 5] with the object??? What does calling that block with object mean?
Let's do it in parts:
1)The class Array was native from ruby, which means we are adding a method to all instances of Array, the method is the eachEven.
2) This method receives as parameter a block to be executed, keep this information in mind.
3) The ["apple", "bad apple", "cherry"] is an instance from Array, which means that we can execute the method eachEven for this array:
array = ["apple", "bad apple", "cherry"]
array.eachEven do |something|
# The do/end block is the parameter passed to the method `eachEven`
# the block will be binded in `wasABlock_nowAProc` in this case
end
4) Inside the method eachEven we get the self (self is the array itself) and execute another method from the Array instance: each (this method iterate over the array binding the current position to the variable inside brackets: |object|)
5) If the condition returns a positive result, it will execute the block inside if, in the case:
wasABlock_nowAProc.call object
# We execute the block of step 2 passing the current position value as a parameter
In fact, if we execute the following code:
array = [1, 2, 3, 4]
array.eachEven do |position_value|
puts "The #{position_value} is even"
end
We gonna get the following result:
The 1 is even # The block `wasABlock_nowAProc` will bind the 1 to the object and print it
The 3 is even # Same here, 3 will be used as the object in the execution of `wasABlock_nowAProc`
Hope it helps
Let's break apart the code here:
['apple', 'bad apple', 'cherry', 'durian'].eachEven do |fruit|
puts 'Yum! I just love '+fruit+' pies, don\'t you?'
end
What we have here boils down to:
receiver.method do |block_argument_one|
# this is the _body_ of the _block_
end
So:
['apple', 'bad apple', 'cherry', 'durian'] is called the receiver (or subject, or just object or instance)
eachEven is the method being called on the receiver
Everything from do to end is the block. It could also be { to } and work the same (well, mostly)
|fruit| is the block arguments list, with fruit being the only argument the block cares about.
puts … is the body of the block
What happens to the block is:
The code in the block gets interpreted, but not run
A placeholder for that code is passed to the method the block is attached to
the method runs, and can access the block while running
Now lets look at how a method that takes a block works:
class SomeClass
def some_method(regular_argument, &block_capture_argument)
# method body
# explicitly call the block:
block_capture_argument.call("first value passed to block")
# implicitly call the block (same as above)
yield "first value passed to block"
end
end
This shows several ways a block can be used:
When you define a method with the last argument beginning with &, a reference to the block is made available to the method by the name after the & (your wasABlock_nowAProc argument, for example). Then your method can do what is likes with the block, maybe calling it, or maybe even storing it somewhere a completely different method can use it.
Alternatively, you can use the yield keyword to call the block implicitly. In that case, you don't need a & argument to the method (but it still works if you do have that argument). Note that ruby allows you to attach a block to any method, regardless of if it uses that block. Methods can check if there was a block with the keyword block_given?, or check that the value of the & argument is present.
When you call the block, either with yield or with call, arguments you give to the call method are passed as arguments to the block.
The method can do whatever it wants with the block. It can call it once, twice, 0, or 300 times. It can call it with the same arguments each time or with different arguments each time.
In your specific example, the block gets called (with the value of object) for each item in the receiver, but only if the isEven variable is true.
Also in your specific example, you are calling the block from inside another block (which provides object for you), but don't let that confuse you.
To summarize:
blocks can be attached to any method using either do … end or {…}
blocks don't run unless the method they are attached to decides to call them
methods get called on a receiver
methods that use blocks get to decide how and when to use them
methods that use blocks can call blocks (or use yield) and pass any number of arguments to the block.
blocks can be defined to use those arguments (with the |…| syntax), and can name those arguments whatever they want (what matters is the order/position of the arguments).

Calling call() on a function in Ruby

I have code as follows:
def sum(a, b)
a + b
end
puts sum.call 2, 3
I get an error like:
wrong number of arguments (given 0, expected 2) (ArgumentError)
How can I call a function?
EDIT
I want to have a function able to call other one with certain arguments. I've written the code like below but the same error is still displayed.
def sum(a, b)
a + b
end
def kall(func, *args)
send(func, *args)
end
puts kall(sum, 2, 3)
In order to invoke the function sum, just delete the .call call:
def sum(a, b)
a + b
end
sum(1, 2)
# => 3
Other way to call the method is doing:
send(:sum, 1, 2)
Which invokes the method sum on the current context/object with the list of arguments (1, 2).
One more way to call a method is:
method(:sum).call(2, 3)
#=> 5
sum is not a function, it is a method. Methods belong to objects, they aren't objects. Ruby is an object-oriented language, which means you can only store objects in variables, only pass objects as arguments (with the slightly odd exception of blocks), only return objects from methods and only send messages to objects.
You cannot send a message to the sum method, because you can only send messages to objects, and methods aren't objects.
And even if it were possible to send messages to methods, there would still be an ambiguity in your code: Ruby allows you to leave out the argument list to a message send if you don't pass any arguments, therefore
sum
is a valid message send and is (somewhat) equivalent (modulo privacy) to
self.sum()
So, even if it were possible to send messages to methods, Ruby would still think that you try to send the sum message without an argument list.
So, since the problem is that we need an object, there are two things we can do. Firstly, we can use an object to begin with.
You used the term "function" in your question. Well, Ruby doesn't have functions, but it has something close to it: Procs. One solution would be to use a Proc instead of a method:
sum = -> (a, b) { a + b }
sum.(2, 3)
#=> 5
The other solution would be to obtain an object for the method. Ruby's Reflection System has a class called Method which responds to call, instances of which are reflective proxies for methods. You can obtain a Method object by sending the Object#method message to an object, e.g.:
sum = method(:sum)
sum.(2, 3)
#=> 5
You don't need to do .call or anything like that. If the function has no parameters, simply just type the name of the function. And if the function does have parameters, just do myFunction(param1, param2).
def hello
puts 'Hello, World!'
end
def output(string)
puts string
end
# Both of these do the same thing:
hello
hello()
# Doing that with stdout won't work though. It expects one argument, string
output('You can do it with parenthesis')
output 'You can also do it without'

How can I get different behaviour from class to_s method for string interpolation syntax?

I've written a class that is acting like an array for another class. I have a to_s method which returns in a similar manner to an array when used as follows:
array = [1, 2, 3, 4]
puts array
This results in the output:
1
2
3
4
When used with string interpolation puts "#{array}" the output is different:
[1, 2, 3, 4]
How can I implement a to_s method in my class to provide both a multiline output for when it is not used in string interpolation and a single line output when it is?
I considered trying to get the caller label with caller_locations(1,1)[0].label but this seems far from optimal. Any help or direction to the relevant documentation would be much appreciated, I haven't been able to find anything on the subject yet. Thanks!
Array#to_s does not (and should not) inspect the context it is called in. The array is printed differently, because puts checks if the given argument is an array (or can be converted to an array). From the documentation:
If called with an array argument, writes each element on a new line.
You can provide a to_ary method:
class MyArray
def to_s
[1, 2, 3].join('-')
end
def to_ary
[1, 2, 3]
end
end
a = MyArray.new
puts a # calls to_ary and then prints each item on a separate line
# 1
# 2
# 3
puts a.to_s # just prints the result of to_s
# 1-2-3
puts "#{a}" # same as above
# 1-2-3

Can an object be changed to an instance of a different class in-place?

A parameter received by a method is a reference to the same object as the argument passed to it, e.g.:
def object_id_of_param(object)
object.object_id
end
the_object = [1, 2, 3]
the_object.object_id # => 15247020
object_id_of_param(the_object) # => 15247020
If you call an instance method of that object that modifies the object in-place from any reference to the object, regardless of scope, the single object that all variables reference is itself modified in-place, e.g.:
def modify_object(object)
object.map! { |n| n + 1 }
end
the_object = [1, 2, 3]
the_object.object_id # => 18047480
the_object # => [1, 2, 3]
modify_object(the_object)
the_object.object_id # => 18047480
the_object.inspect #=> [2, 3, 4]
If you want to do something else to that object and a method like Array#map! is not available to do it, you can use Object#instance_variable_set to change it (see ndn's great answer).
If you try to change it to a different class entirely, it doesn't work; the following method does not modify the original object. It does not turn it from an Array into a Time; it causes the new object variable in the method to reference a new Time object:
def fail_to_modify_object(object)
object = Time.now
end
the_object = [1, 2, 3]
the_object.object_id # => 14554660
the_object.inspect #=> [1, 2, 3]
fail_to_modify_object(the_object)
the_object.object_id # => 14554660
the_object.inspect #=> [1, 2, 3]
The example of turning an Array object into a new Time is contrived, but you get the idea.
Is it possible to change an object, referred to by an arbitrary number of variables, into an instance of a different class?
Bang methods aren't necessary always modifying objects in place.
Bang is just a convention that says this version of the method is "more dangerous" than the one without the bang. For example, in rails many bang methods raise an exception instead of returning nil.
Also the fact that a method doesn't have a bang doesn't mean it doesn't modify the object (for example Array#pop).
As to how to modify an object that doesn't provide any methods that mutate - you can use Object#instance_variable_get and Object#instance_variable_set:
class X
attr_reader :foo
def initialize
#foo = :bar
end
end
x = X.new
x.foo # => :bar
x.instance_variable_set(:#foo, :baz)
x.foo # => :baz
However, note that if the someone didn't give you direct access to something, 99.99% of the time you are doing something wrong if you are trying to.
To modify an object retaining its identity (object id), the new object must at least belong to the same class as before; otherwise, it does not make sense. It is impossible to modify a Time instance into an Array instance or vice versa retaining its identity.
Or, if you meant in your second example to simply change the reference of the_object from a Time instance into an Array instance, then that is a matter of scope of local variables, and has nothing to do with immutability.
You need to distinguish between a variable and an object.
In a method, you get access to the object. You don't get access to the variable that is defined outside the method.
If you write object = Time.now, you are not changing the object, you assign another object to the variable object. The old object still exists.
The variable outside your method still holds the old object.
You can change the object by sending messages to it. If you have an array object, you can see your variable the methods here: http://ruby-doc.org/core-2.2.0/Array.html Some of the methods change the array, some methods just return something.
You can do things like object[0] = Time.now . Even this is implemented as method call to []= . It will change the first element of your array-object to now hold a time object. So you are quite free to change your array object.
If you are advanced, you can even add new methods to your array-object, so that it may behave like a time object. But that is almost always not a good idea.
The variable only exists where it is defined.

Resources