The main programming language in my new job is Ruby. Thus I have to spend some time learning it. As a Go and Python programmer, some features in Ruby really shock me. I have it recorded here.
I am an advocate of typing. As there are type hints in Python, I think some similar hints may be in Ruby. The most popular ones seem to be the offical rbs and the Sorbet,
The syntax of rbs is similar with Python’s. However, rbs has to be written as separate files and it only works as hints.
module ChatApp
VERSION: String
class User
attr_reader login: String
attr_reader email: String
def initialize: (login: String, email: String) -> void
end
end
Sortbet supports type checking. However, the syntax confused me.
# typed: true
extend T::Sig
sig {params(name: String).returns(Integer)}
def main(name)
puts "Hello, #{name}!"
name.length
end
I mean, the syntax is very clear and easy to integrate it into codebase.
However, I could not figure out how the sig
worked. How does a function
call affects a following function definition? In Python, decorators
works the same way but it is defined by a specific syntax instead of a
simple function call.
Let’s investigate how sig
works.
module T::Sig
def sig(arg0=nil, &blk)
T::Private::Methods.declare_sig(self, Kernel.caller_locations(1, 1)&.first, arg0, &blk)
end
end
module T::Private::Methods
def self.declare_sig(mod, loc, arg, &blk)
T::Private::DeclState.current.active_declaration = _declare_sig_internal(mod, loc, arg, &blk)
nil
end
private_class_method def self._declare_sig_internal(mod, loc, arg, raw: false, &blk)
install_hooks(mod)
# ... ...
end
def self.install_hooks(mod)
# ... ...
if mod.singleton_class?
mod.include(SingletonMethodHooks)
else
mod.extend(MethodHooks)
end
# ... ...
end
module MethodHooks
def method_added(name)
super(name)
::T::Private::Methods._on_method_added(self, name, is_singleton_method: false)
end
end
end
We can find an interesting function name, method_added
. What will happen
if we extend a class with a method_added
function?
module Hook
def method_added name
super name
puts "method_added is called with parameters #{name}"
end
end
class Foo
extend Hook
def foo; end
end
After running the code above, “method_added is called with parameters foo” will be printed.
Now it is clear how the sig
checks signatures of functions in runtime:
method_added
callback method.There are some other callback functions in Ruby, like included
,
extended
, prepended
, inherited
, method_missing
, which would be
helpful when implementing complicated features.
I was quite impressed by those callback methods in Ruby, which indicate that the behavior of the program is generated in runtime instead of in compile time. It is very different from other languages.