When you define a method, it returns a symbol with the same name as the method. Is there a point to this? Or is it just there as validation that you created it?
Like so:
def something
...
end
# => :something
IRb always displays the result of calling inspect on the value of the last expression that was evaluated. It doesn't matter whether that expression is a literal expression, a conditional expression, a message send, a class definition expression or a method definition expression.
Everything returns a value in Ruby, i.e. everything is an expression, there is no such thing as a statement in Ruby.
In the past, the return value of a method definition expression was undefined. Most Ruby implementations simply returned nil from a method definition expression, but Rubinius for example returned the CompiledMethod object for the method that was defined.
With Ruby 2.1, the return value of a method definition expression was standardized to be the Symbol corresponding to the method's name. This allows you to use the method definition expression as an argument in methods that expect the name of a method as an argument.
Some examples:
# Before Ruby 2.0:
def foo; end
private :foo
# After Ruby 2.0:
private def foo; end # similar for `protected`, `public`, `module_function`
# Before Ruby 2.0:
def map; end
alias_method :collect, :map
# After Ruby 2.0:
alias_method :collect, def map; end
On a personal note, I would have preferred a method definition expression to evaluate to an UnboundMethod object corresponding to that method, and methods like public, private, protected, alias_method, module_function etc. should be amended to accept UnboundMethods in addition to Symbols and Strings.
The person who proposed this had in mind a usage like this:
private def foo
...
end
protected def bar
...
end
Methods such as public, private, protected take symbols as arguments. The point was to make use of this syntax.
All method defs return symbols in Ruby >=2.1 (not just the ones in IRB).
For example:
class Foo
p def bar; end
end
# => prints :bar
Why is this interesting?
You may have noticed that there are many methods, particularly class-level methods, that take the symbolized name of another method as an argument. You may be familiar with before_filter in Rails controllers. Since method defs return symbols, you could potentially do this:
class MyController < ApplicationController
before_filter def my_filter
# do stuff
end
end
IRB respects the ruby standard “the result of last executed statement is returned from method.” Imagine the code:
def a
def b
# do stuff
end
end
What is the result of execution this code? It follows:
a
# => :b
a.class
# => Symbol < Object
That said, IRB executes the method definition and returns/prints out it’s result. Which is, apparently, a Symbol instance.
Related
A double colon(::) allows constants, instance methods, and class methods defined within a class or module, to be accessed from anywhere outside the class or module.
Looking at this example:
class Sample
VAR_SAMPLE="what is the difference?"
def self.show_var
return VAR_SAMPLE
end
def method2
return VAR_SAMPLE
end
end
puts Sample::show_var # => what is the difference?
puts Sample.show_var # => what is the difference?
puts Sample::new::method2 # => what is the difference?
puts Sample.new.method2 # => what is the difference?
What is the difference in accessing class method using dot(.) and a double colon (::) operator then? Any thoughts are appreciated.
The double colon :: namespace operator can also be used as a message sending operator. In other words,
foo.bar
can also be written as
foo::bar
Except when not.
In particular, . is always a message send. :: is usually a namespace lookup, except when it cannot possibly be. That means, for example, you cannot call a message that starts with an uppercase character, unless you also pass an argument list.
foo = Class.new do
def BAR; :method end
BAR = :constant
end
foo.BAR #=> :method
foo::BAR #=> :constant
foo::BAR() #=> :method
The fact that :: can also be used for message sends is a historical curiosity, and is banned in most style guides except for "class factories", i.e. methods that return classes. Imagine a web framework that is set up like this:
module Controller
def self.R(path)
Class.new(AbstractController) do
# a bunch of methods for dealing with routing to `path`
end
end
end
class IndexController < Controller::R '/index.html'
def get
render 'Welcome'
end
end
In this case, in some style guides, it would be acceptable to write Controller::R because even though R is a method, it returns a class, so it kind-of acts like one.
But this is a special case for certain DSLs and is only allowed in certain style guides. Most style guides disallow :: for message sends, because it is redundant with ., because it already has a another different meaning (namespace resolution), and because it doesn't behave like . in all cases.
What is the difference in accessing class method using dot(.) and a double colon (::) operator then?
On the one hand, you can say, there is no difference because when used as the message sending operator, they both do the exact same thing.
On the other hand, there is a difference in syntax, namely that foo::BAR isn't a message send, it is a namespace lookup which is completely different. from foo.BAR, which is a message send.
You can call ruby methods using following ways
using Dot (.), Double Colon (::), send method & method method
class Sample
VAR_SAMPLE="what is the difference?"
def self.show_var
return VAR_SAMPLE
end
def method2
return VAR_SAMPLE
end
end
puts Sample::show_var
puts Sample.show_var
puts Sample.send(:show_var)
puts Sample.method(:show_var).call
puts Sample::new::method2
puts Sample.new.method2
puts Sample.send(:new).send(:method2)
puts Sample.method(:new).call.method(:method2).call
# All the above will return `what is the difference?` only
Now consider method which is a private
class Sample
VAR_SAMPLE="what is the difference?"
private
def self.show_var
return VAR_SAMPLE
end
end
puts Sample::show_var # Will throw error private method `show_var' called for
puts Sample.show_var # Will throw error private method `show_var' called for
puts Sample.send(:show_var) # what is the difference?
puts Sample.method(:show_var).call # what is the difference?
Note:- Other than this you can call ruby methods using other metaprogramming methods as well.
I want to instantiate an object from a class I wrote on a different file. What I got is wrong number of arguments (given 1, expected 0) (ArgumentError)
Here is the main code
# ./lib/parking_lot
require_relative './lot.rb'
class ParkingLotInterface
def initialize(input: $stdin, output: $stdout)
#input, #output = input, output
#lot = nil
end
def prompt_input
#lot = Lot.new(10)
end
end
parking_lot_interface = ParkingLotInterface.new(input: $stdin, output: $stdout)
parking_lot_interface.prompt_input
And here is the object class
# ./lib/lot
class Lot
attr_reader :slots,
def initialize(size)
#slots = Arrays.new(size)
end
end
The error was thrown at the line where I tried to instantiate a new Lot object. Looking at the internet, people who had the same problem got told that they didn't specify def initialize in the class, or they mistyped it. However, I did what they all said and I still faced wrong number of arguments (given 1, expected 0) (ArgumentError)
What did I do wrong?
In Ruby, method definitions are expressions as well (in fact, in Ruby, everything is an expression, there are no statements), so they evaluate to an object. Method definition expressions evaluate to a Symbol denoting the name of the method that was defined.
So,
def initialize(*) end
#=> :initialize
In your code, you have a comma after attr_reader :slots, which means that you pass two arguments to attr_reader, namely the symbol :slots and the expression def initialize(…) … end. Since Ruby is a strict language, the arguments to attr_reader will be evaluated first, before attr_reader itself is executed.
So, what happens first is that the method definition expression gets evaluated. This defines a (private) method named initialize. It also evaluates to the symbol :initialize.
Next, the expression attr_reader :slots, :initialize gets evaluated, which defines two methods named slots and initialize, thus overwriting the method you just defined. Note that this will print a warning:
lot.rb:3: warning: method redefined; discarding old initialize
lot.rb:5: warning: previous definition of initialize was here
You should always read the warnings, the Ruby developers don't spend the hard work putting them in just for the fun of it!
The solution is to remove the comma telling Ruby to look for a second argument.
There is a second error in your code, namely that you misspelt Array within Lot#initialize.
And, there are a couple of stylistic improvements that you could make:
There is no need to pass a path and a filename extension to require_relative. It should be require_relative 'lot'.
Un-initialized instance variables evaluate to nil, so there is no need to initialize #lot to nil.
$stdin and $stdout are the default argument values of the stdin: and stdout: keyword parameters, so there is no need to pass them explicitly.
It is seldom necessary to create an array of a specific size, since Ruby arrays are dynamic and can change their size at any time.
With all this taken in to account, your code would look something like this:
# ./lib/parking_lot
require_relative 'lot'
class ParkingLotInterface
def initialize(input: $stdin, output: $stdout)
#input, #output = input, output
end
def prompt_input
#lot = Lot.new(10)
end
end
parking_lot_interface = ParkingLotInterface.new
parking_lot_interface.prompt_input
# ./lib/lot
class Lot
attr_reader :slots
def initialize(size)
#slots = Array.new(size)
# could be #slots = []
# depending on how you use `#slots` later
end
end
Delete the comma after
attr_reader :slots,
it would be
attr_reader :slots
And take a look, you are trying to instance Arrays (and must not to be in plural) on lot.rb
def initialize(size)
#slots = Arrays.new(size)
end
it would be
def initialize(size)
#slots = Array.new(size)
end
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.
use ruby version is 2.0.0p648
I want extend File Class returns change "extend". But returned method returns is normal function.
Why returned normal function?
class File
alias_method :__open__, :open
def open()
'extend'
end
end
p File.open('test.txt')
#<File:test.txt>
class File
class << self
alias_method :__open__, :open
def open(*)
'extend'
end
end
end
File.open('test.txt') # => "extend"
File.__open__('test.txt') # => #<File:test.txt>
Explanation
File.open is a class method, yet you are aliasing and redefining at the instance scope. To alias a class method, you will need to do so on the singleton class. You can do this with the syntax class << self; end. To oversimplify things, accessing the singleton class essentially lets you use instance level syntax at the class scope, so you can also define class methods there without preceding the method name with self. e.g. self.open
Once you're redefining File.open you'll want to respect the API of the original method as pertains to arguments. If your overriding method doesn't use any arguments as in your example, then you can give a splat * operator as the single parameter. This means that the method can take 0 or more arguments without throwing an error, but they won't be used in the method body. Otherwise, if you define the method with the signature def open() (or the equivalent and stylistically preferred def open) then you'll get an ArgumentError when you call Foo.open('test.txt') because you're passing more arguments than the method expects.
class File
class << self
alias_method :__open__, :open
def open
'extend'
end
end
end
File.open('test.txt') # => ArgumentError: wrong number of arguments (given 1, expected 0)
This method:
def format_stations_and_date
from_station.titelize! if from_station.respond_to?(:titleize!)
to_station.titleize! if to_station.respond_to?(:titleize!)
if date.respond_to?(:to_date)
date = date.to_date
end
end
Fails with this error when date is nil:
NoMethodError (You have a nil object when you didn't expect it!
The error occurred while evaluating nil.to_date):
app/models/schedule.rb:87:in `format_stations_and_date'
app/controllers/schedules_controller.rb:15:in `show'
However, if I change date = date.to_date to self.date = self.date.to_date, the method works correctly.
What's going on? In general, when do I have to write self?
Edit: It's not related to the question, but please note that there is no "titleize!" method.
Whenever you want to invoke a setter method on self, you have to write self.foo = bar. If you just write foo = bar, the ruby parser recognizes that as a variable assignment and thinks of foo as a local variable from now on. For the parser to realize, that you want to invoke a setter method, and not assign a local variable, you have to write obj.foo = bar, so if the object is self, self.foo = bar
You disambiguiate between the instance method name and a local variable using self (it is allowed to have both with the same name in the same scope). In other words, there will be a method name resolution only if there is no local or block variable of the same name in scope. Behold:
class Foo
attr_accessor :boo
def do_boo
boo = 123
puts "Locvar: #{boo} Method: #{self.boo}"
end
end
Foo.new.do_boo
Here's why: imagine you have a module which implements a method. This method assigns something to it's internal local variable
"foo" which is used for some computation. If you skip the "self" part, the method will make a "foo=" method call on the object
whose class includes the module, which was not the intention of the author and can be downright disastrous.
class Foo
def bar=(new_value_of_bar)
set_off_nukes(new_value_of_bar / 3)
end
end
module InnocentModule # written by a different author elsewhere
def do_useful_stuff
...
bar = Math.sin(something) # we're dead
end
end
Foo.send(:include, InnocentModule)
Another crucial part where you have to use self is when invoking the Object#class method, because simply saying "class" means a class keyword for Ruby.