00:00:16.080
thank you all right so
00:00:22.199
um yes an honor to be here it's my first time in South America and I am very
00:00:29.599
excited and very very thankful to have been invited to talk in this amazing conference and also there's a nice
00:00:36.880
personal toach in in which this is the land of Raphael which is cool really
00:00:43.600
cool excited to be here um I would like to thank the RA
00:00:51.280
foundation because as part of their uh partnership ship with tropical on rails
00:00:56.800
they assisted me h in my in my trip to Brazil so thanks the rails foundation
00:01:02.640
for that all right so uh in this talk we are going to have uh
00:01:10.560
an overview of a few things related to Ruby that maybe are not so common in our
00:01:16.720
day-to-day programming right uh I am going to showcase them in the
00:01:22.560
context of Zyberg but that's just like an excuse to talk about a few things Ruby all right so this uh talk has se
00:01:30.560
seven sections fasten your belts uh the first five of them are going to be technical
00:01:37.040
and then the the last two of them not so much all right so uh in the context of
00:01:45.040
cyber we are going to talk about constants and to make sure that we are all on the same page about some
00:01:50.960
fundamental things about constants we are going to do uh a very quick recap uh
00:01:57.200
talking about things that are important to have in mind all right so the first thing is that the class of module
00:02:02.640
keywords store classes of modules in constants which is something very unique
00:02:07.680
in the Ruby programming language all right so when we do class C both things are more or less
00:02:14.879
equivalent all right so that thing is creating a class object and storing the
00:02:20.400
class object in a C constant same with a module that thing is um creating a
00:02:26.480
module object and storing the module object in an M constant and that M constant is a regular constant it has
00:02:32.879
nothing special about it okay now the next thing is that Ruby does not have
00:02:38.560
syntax for types and uh if you come from a different programming language that
00:02:43.680
has types um this is important this distinction is important to have in mind
00:02:49.280
when you work in Ruby so for instance here we have project.find something
00:02:56.200
right so the key observation is that project is not a type project is a
00:03:01.440
regular constant okay so here project is a constant that that's an expression
00:03:07.440
that evaluates to a class object okay and that object happens to respond
00:03:12.959
to the fine method that is what is working uh for real in that slide okay so every time you see something like
00:03:20.080
that forget about types forget about a bake concept of classes that's a regular
00:03:25.920
constant okay now another thing that is very unique in the Ruby programming language that is that constants belong
00:03:33.599
literally to class and module objects okay so every class of module object in
00:03:38.720
your program has like a hash table you could think that stores constant names
00:03:45.280
to the values that are that are associated with them okay this is literally happening in your in your in
00:03:52.000
your in your Ruby program all right so they are collections of methods and collections of constants as well and
00:03:57.599
this is very unique to Ruby in the case of top level constants uh they are not except they are not exceptional in that
00:04:04.239
sense where they are stored in object okay so here we have the first one is a
00:04:10.560
top level constant Ruby engine that's a string the second one we can think is a
00:04:15.680
class that's a top level constant as well okay the the third one is the constant
00:04:23.320
pi that is stored in the math module so the first two ones are stored in object
00:04:29.280
the the last one is stored in the math module all
00:04:34.360
right you can you we can introspect this okay so it it could be surprising to to
00:04:40.880
see that when you boot the interpreter the top level has more than 100 constants defined okay and you can you
00:04:47.840
can introspect them in the second in the second um prompt we have that okay so if
00:04:55.280
you list the constants in object there's there's API related to constants in cluster modules you will see things like
00:05:01.759
file kernel those are symbols because is the name of the constants constant names are symbol okay so you see it's file
00:05:10.080
kernel string what is that isn't string like a class no when you write a string
00:05:16.720
in in your source code that's a constant okay and you can see here that's a constant stored in object okay what
00:05:23.120
happens is that when you put the interpreter the interpreter builds the string class it's it assigns the string
00:05:29.440
class to a to a constant call it string you know and you have it okay but you
00:05:34.960
can see here that they are constants in object all right
00:05:40.039
enough so with that we can pass to an overview of how Zyberg autoloads okay
00:05:47.680
because um I believe that it's it's better to uh program when you know your
00:05:53.680
tools okay that's better than having your tools to be black boxes so so if
00:05:58.960
you at least understand more or less what the tool is doing on your behalf uh that's more transparent we we could say
00:06:06.240
okay so I'm going to just explain very briefly how to how it autoloads it does more things okay we we can eager load we
00:06:13.120
can reload and we have different features okay but the key the key aspect is autoloading so how do we
00:06:20.440
autoload uh there's this API in Ruby this is not zy this is this comes with
00:06:25.919
Ruby module autoload okay module autoload allows you to define constants to be allow loaded on demand so this is
00:06:34.160
an example from active record it it is not pervatim like this but conceptually is like this okay and we say module
00:06:41.759
active record okay we say autoload base uh with that second argument what what
00:06:47.840
this is doing is by now I am defining the top level
00:06:53.199
module active record okay and I am setting this thing because whenever I
00:06:58.960
want to refer to active record column column base if the constant is not
00:07:04.240
defined at that point this is telling the interpreter please go do a require
00:07:10.560
on the second argument And in the happy path that require as a
00:07:16.160
side effect has defined it active record colon column base the program resumes
00:07:21.199
and you know you you got the the constant outload by Ruby all
00:07:26.680
right okay so uh the main idea behind Zyberg is to it's Zyber is fully fully
00:07:35.120
based on this API uh we start with configuration and the
00:07:40.560
most important one is where is your code and in rails we call that the autoload paths okay in zyber that's called it the
00:07:48.400
root directories but we have different nomenclature in rails so we have the
00:07:54.080
autoload paths the those are a models a controllers you know a helpers and uh
00:08:01.280
the convention is that those paths are storing files uh and they represent the
00:08:06.639
top level name place so a models userb is expected to define top level user
00:08:12.720
right so we have that configuration in place and we do the following zyber
00:08:18.400
before you start using your code scans the autoload paths one level okay it
00:08:24.319
scans it leads the directories now for each one of the files that you
00:08:31.360
have there you get uh you get the file the base namealized with the extension
00:08:37.279
removed so from user lowercase dollarb we remove the extension we come we get
00:08:43.200
user with U capital with capital U right and we define autoload because now we
00:08:48.880
have the name of the constant that is expected to to be defined in that file
00:08:54.399
and we have the the the file that is supposed to define that constant so we have the necessary information to put an
00:09:01.279
autoload in this case in object because it's a top level constant okay so we set
00:09:06.480
we we scan we set autoloads done and it does nothing else it waits and you are
00:09:12.720
ready to start referring to any constant at any point in your project no matter
00:09:17.760
how deep okay now if you refer to a name space then we do the same thing for that
00:09:23.680
name space so so if you refer to a name space then there's code that intercepts that scans camelize set autoloads in
00:09:32.399
that name space and you you know the autoloader you know scans sub trees of
00:09:38.320
your project tree uh as they are used so this is the basic the basic idea a scan
00:09:44.480
camelize set autoload and let the user uh refer to things as they are so these
00:09:51.279
are examples okay we have here uh userb permission.b those are models you know
00:09:57.120
in this example controller users controller and we have permissions controller in an admin name space so
00:10:04.120
uh Zyberg on your behalf is going to do this you could you could write this by
00:10:10.080
hand okay but this automated for you is doing dynamically so we set an autoload for user an autoload for permission and
00:10:16.240
autoload for user controller that's the top level okay wait it does nothing else
00:10:21.600
no if you do not refer to admin it's going it's going to ignore admin but if
00:10:27.040
you refer to admin then it's going to create an admin module on your behalf
00:10:32.440
automatically and we do this as we saw in the previous slides we can define a
00:10:37.519
module and assign to the admin constant that is done dynamically so it's done with con cons get not like this but it's
00:10:44.079
the same idea and now that we have the admin module created we can set an
00:10:49.680
autoload for permissions controller and repeat that's the basic idea of Zybre okay if you understand
00:10:56.399
this you have Zyber in main you know you know how it works then from here to the gem there's some work to do but but if
00:11:04.079
if you understand this uh yes uh you have a good model of how it
00:11:09.560
works all right so that's how it works and Zyber has an a very fundamental
00:11:14.959
property which is that it's thread safe um let's talk a little bit i have
00:11:21.279
never explained it why Zyber is in thread safe um so let's start with this with
00:11:29.120
this observation class of module definitions are not atomic in Ruby okay
00:11:35.200
so let's imagine we have this model we have um a user class has many post
00:11:43.120
has a validation and has um class method for a special uh user right the root
00:11:50.279
user okay so this does not happen in one
00:11:58.360
shot this happens step by step first the class is defined in the body in the body
00:12:04.480
definition ruby sets self to the class object so at the start of the body
00:12:10.000
definition we have already the user class and it has no associations it has no validations it has no methods but it
00:12:17.360
exists and you can use it okay so when we say that in Ruby class and modules
00:12:23.600
are open sometimes that comes with the image of uh I can open this class in a
00:12:29.600
different file or I can I can monkey patch that gem to fix a back you know or
00:12:35.279
something like that you know it's way easier than that here we are using this property so the class is defined and we
00:12:43.360
are modifying the class because the class is open so in the in the first line in the body we But we are modifying
00:12:50.399
the the class we are adding an association then we are adding validation at that point there's no method except for the ones inherited
00:12:57.839
okay but then we are defining because the class is open all right we are seeing here the class is open and this
00:13:04.399
this is happening step by step indeed those that has many is a method all
00:13:09.920
right it's a method inherited so we are using the class all right now let's consider this we have
00:13:17.200
two threads okay in one let's imagine this is not rails okay this imagine we are using active record directly outside
00:13:24.000
of rails so we have to do requires manually so the first thread is doing wants to use the user it requires the
00:13:31.440
file and then the second thread also wants to access the this special
00:13:38.760
user all right so what happens here this is not atomic so perhaps after the hus
00:13:46.639
many uh association has been uh defined maybe we have a context switch
00:13:53.279
that's possible maybe we have a context switch so what happens let's imagine
00:13:58.639
that the first thread okay let's imagine that the first thread gets to run first
00:14:05.199
it requires the file and then after the h has many there's a context switch and we go to the second
00:14:11.399
thread in that case what happens the user constant already exists
00:14:16.880
the class already exists however we have not reached the point in which the method was defined so we are going to
00:14:23.279
have a no method error in that case all right so this is possible maybe because
00:14:28.880
if the context switch does not happen we don't see the error so this is a race condition and you know is a is is a is a
00:14:36.399
Heisenber back that you know is the worst the worst type of backs because it happens randomly all right so you know
00:14:43.040
and the problem here is that this pro this program is incorrect you should have a require in the second thread okay
00:14:50.480
and precisely this is the one of the motivations for Zyber to not have to think about these things okay all right
00:14:58.680
so now we are in a situation in which we are doing autoloads and I am claiming that this is
00:15:06.000
user that this is thread safe and this is what our race applications do every day
00:15:11.959
okay how is how is it possible may because if the if if we can think if the
00:15:18.160
first thread goes and autoloads user by the same
00:15:24.160
principle we could have a context switch and go to the second thread but what if
00:15:29.600
the method has not been defined is is it the same situation well it it is but we
00:15:34.959
have help from the interpreter because the interpreter synchronizes constant
00:15:40.600
access so basically if there's a context switch and you get
00:15:46.360
here the interpreter says "Ah user yes I have the constant but there's an
00:15:52.320
autoload for this constant happening right now that has not finished." So the
00:15:57.360
interpreter is going to block here it's going to pass and it's going to
00:16:02.560
wait for the autoload that is running to finish once that is finished you
00:16:09.240
resume so the previous autoloading race was not thread safe by itself it was
00:16:15.360
thanks to help from the framework there was logs and stuff doing so you could uh
00:16:20.800
you could autoload in a thread safe manner in in two requests for instance in two jobs okay but if you had an
00:16:29.120
script uh executed by by red runner and you had multi threads there the the
00:16:35.040
autoloading was not thread safe okay in order to make that thread safe you you you would need to add stuff from the
00:16:40.959
framework to put that that coordination in you know in um in place but with
00:16:47.680
Zyberg we do not need this and we can use Zybre in in regular code without any
00:16:52.880
locking or anything thanks fundamentally to this property of
00:16:58.040
Ruby all right uh in Trafford Ruby this is not the case yet okay trafford Ruby
00:17:04.319
uh doesn't have this synchronization in mind uh as uh implemented I I would say
00:17:09.760
okay but uh yeah that's the only caveat okay okay in if you are working with traffic Ruby you would need to eager
00:17:15.839
load for instance or work with only one one single you can still autoload but but you cannot do it in a in a uh with
00:17:23.039
multiple threads all right but yes in in J Ruby and in C Ruby this is working all right now let's go with
00:17:30.799
another topic which is how to generate constant paths so this is the most
00:17:35.840
important data in Zyberg the Cref the constant reference okay we said
00:17:41.360
constants belong belong to class of module objects so the most uh the most
00:17:50.120
um the abstraction with the most fidelity that we can get to represent that is the this pair the pair mod C
00:17:57.840
name which means this this constant name okay this constant is stored in this
00:18:03.799
module okay module would be a class or module okay C name would be a symbol
00:18:10.400
this is the most important data okay since uh The super class of the class
00:18:15.520
class is module okay so uh we can simplify this and say just this is module all right
00:18:22.679
so mod is a short variable that is ideatic to use to be used when you want
00:18:29.360
to hold class and module objects without distinction all right okay this is the most important
00:18:36.559
data so let's see uh some examples okay top level user how do we represent top
00:18:43.360
level user this way so it's a top level constant therefore belongs to object so
00:18:49.360
the first coordinate would be object the second coordinate would be the symbol user right math pi pi that is stored in
00:18:58.320
the math module let's imagine API v1 inventory item okay so item would be the
00:19:05.440
name of the constant and it would be stored in the module API v1 inventory
00:19:10.960
all right but don't don't get confused by this notation okay because what we
00:19:17.919
have here is one object let's forget about the columns and everything is one one object okay so we have one object
00:19:26.320
and a symbol all right so the exercise that we want to do is to write um a cpath method that
00:19:35.360
returns a string with a constant path for a given constant reference okay so
00:19:42.480
for instance the C path of this pair object user could be user top level
00:19:50.320
thing okay we we want the constant path of a string okay in top level we do not
00:19:55.520
put object you know before that so this is the one that we want so we can start
00:20:01.120
writing our method okay c path well if object if the if the first argument is
00:20:07.720
object then return the symbol as a string done easy okay we have the first
00:20:14.720
the first part done how about the other ones okay cath of math pi would be that
00:20:21.520
constant path c path of this thing would be that constant path all right
00:20:27.200
intuitive right okay so we have the last segment
00:20:33.919
we have the last segment as an argument we have pi and we have item so that one is easy what about the the previous the
00:20:40.320
prefix okay how can how can we generate that we we can do it with module name
00:20:46.880
this is a this API in modules all right so if you ask math for the name so math
00:20:54.240
here is is a constant evaluates to a module object that module object responds to name and name returns the
00:21:01.120
string math okay and this is something uh uh stored by the interpreter when the
00:21:07.440
module is defined the interpreter defines an attribute on that module that says your permanent name is math okay
00:21:15.039
that's set by the interpreter there's no public API to change that once set is done okay same for the other one all
00:21:22.000
right so well the else clause could look like this all right we have mod we ask
00:21:28.559
for the name that is going to give us the the prefix and then we f we have colon colon we interpolate the symbol we
00:21:36.320
get uh the constant path that we want and this works but we can improve a
00:21:41.919
couple of things here the first one is that symbol name since this this this um
00:21:48.400
um method exists since Ruby 3 symbol name is a special a special um method in
00:21:55.799
symbol that uh is more efficient because here we are creating a string for the
00:22:02.799
CNAME every time we are we get called so Came toS is get is is is uh creating a
00:22:08.559
string ano string ano string okay same with interpolation in the else clause so
00:22:14.240
symbol name is specialized to be more uh performant if you can leverage this so
00:22:20.480
it returns the string that we expect but that one is frozen and that one is always the same
00:22:25.960
object so by doing this we allocate less strings which is something that is uh
00:22:32.080
always you know good good to have allocate as as as less objects as
00:22:37.360
possible all right especially in in gems like this which are like uh framework
00:22:43.520
level let's say you want to be you want to you want to put the less possible
00:22:49.200
overhead on on the code that is using you right so in this I go the extra mile to
00:22:57.360
do things to be as performant as possible maybe in my regular code I wouldn't go so far but in this I want to
00:23:04.000
make sure I am as performant as possible so less objects now we have it doesn't
00:23:09.200
it doesn't it's not apparent but in mod name we have a problem we have a problem
00:23:14.840
because the name the name method can be overridden okay I have here two real
00:23:21.559
examples so uh for some reason this identity cipher name is
00:23:27.559
identity this this thing they are classes okay this uh cipher thing and this text thing they are classes but for
00:23:34.559
some reason in these gems these things are overridden it doesn't even return a
00:23:40.799
string it gives you a symbol right so we have a problem because in this
00:23:46.480
implementation this is this is this is going to work generally but the moment you open source this and you know a v
00:23:53.840
variety of projects are using it you're going to find this so your c path is
00:24:01.080
broken so if we cannot rely on the name what can we do something that normally
00:24:07.760
you do not use in your day-to-day which is to use module instant method for
00:24:14.279
instance instant method returns a class maybe you have seen this class on bone method and maybe you have seen on method
00:24:21.360
why do why do I want an unbone method okay an unone method so you know that in
00:24:26.960
methods you get a self all right that self is is set
00:24:32.080
by the interpreter for you implicitly okay you know in Python for instance the
00:24:37.840
the self is explicit but still interpreter is is you know in the general case interpreter is setting that
00:24:43.600
first argument on your behalf okay so you have a self anode method is a method
00:24:49.919
that represents the implementation but that is spending to have a self in context it's like a
00:24:56.880
template let's say okay all right the interesting observation is that you are
00:25:02.559
this is not subject to inheritance or to overrides if you if you fetch an
00:25:08.080
instance method it's the original one so we can fetch a model module instant
00:25:13.679
method the meth the name method and that is the original one i I store that in a
00:25:19.200
constant and then I have this helper real mod name that for a given mod okay
00:25:25.600
with bindal by bindal is the API to set the self on that method all right so
00:25:30.799
with this thing we are sure that no matter if the name method is overridden
00:25:36.559
we can fetch the original module name and now we have a good one this one is
00:25:42.279
inside all right now constant references as hash
00:25:48.320
keys new section so as I said this is the most important data in cyborg the constant
00:25:54.880
reference uh I need to uh use constant references
00:26:04.799
uh in some structures in some hash tables and it would be very natural for me to use the constant reference as a
00:26:12.559
key for instance I have a structure that that remembers the autoloads that the
00:26:18.240
the gem has set so this is saying I I I have set an autoload on the user constant okay and I am using at the
00:26:25.840
beginning of of the project I was using constant paths for this but the constant
00:26:32.159
path is not a good a good choice that's not that's not the right abstraction why because a constant path
00:26:40.480
does not uniquely represent a constant indeed it can be the case that the
00:26:46.400
constant has no constant path so you can have constants with no constant path
00:26:51.760
referencing to them with one constant path which is the common theme but you can have more than one constant path and
00:26:58.400
you can have infinite constant path let me show you examples okay this one this module is anonymous it has no it has no
00:27:06.760
constant but again we store constants in modules we do not store constants in
00:27:13.039
constants we store constants in modules so here we are storing the constant in the module right which constant path
00:27:19.600
gives gives me x non there's no constant path that's that's a pure constant is
00:27:26.080
stored in a in a module object that's the right abstraction the module and the constant name right here I have two
00:27:33.840
constant paths okay so there's a module a class the class includes the module and therefore the way lookup works m
00:27:42.400
means that I can refer to that x constant as m x and also as c x so it's
00:27:49.440
two constant paths representing referring to the same one
00:27:55.080
constant now three so we have x and then
00:28:00.760
remember again the same top the same the same theme again the m is not a type is
00:28:07.279
a constant that stores a module object therefore I can do a constant assignment and store that object in other two
00:28:14.320
places so we have m and I say okay I want to store this thing in q and also
00:28:19.760
in p all right what does that mean that mx px and qx is the same thing it's the
00:28:26.960
same object there's no there's no even a lookup argument here it's the same object so we have three constant paths
00:28:36.720
that point to the same thing right now let's let's go with
00:28:43.080
self-referential a little bit self-referential because we have module m x1 m stores the module
00:28:51.640
itself hey this is funny so MX all right
00:28:57.039
good one the normal one but then in the second line we have
00:29:02.760
M what is happening there what is happening is the following we have the top level
00:29:16.159
the module is the module object do you have an X constant yes I have one there
00:29:22.880
you go and you can repeat this as much as you want so m MM we can we can have
00:29:29.600
theoretically an infinite number of constant paths pointing to the same constant
00:29:36.159
and if you think that this is a contrive examples I am happy to say that it is not because this is what happens with
00:29:43.080
object because object the constant object is a top level constant where top
00:29:48.880
level constants are stored in object right so uh let's imagine we have
00:29:55.039
a string which is a top level constant uh so uh object do you have a constant
00:30:01.520
call it a string yes tople constants are stored in object so the first one is
00:30:06.559
kind of normal but what about the the other one well object you you have that's a that's
00:30:14.399
a class object do you have a constant call it object yes it's a top level one so I have it all right so do you have
00:30:22.399
and do you have a string constant yes I have one awesome so you can put as many objects as you want all right again an
00:30:29.520
infinite number of constant paths pointing to the same constant so the the
00:30:34.720
the the coral area of of all this is that the constant path is not a good abstraction for a constant the good
00:30:41.760
abstraction is the module that that stores the constant and the constant name done that's it all right so let's
00:30:49.960
think for a moment all right moment of rest
00:30:56.279
okay so from here I would like to go to here this is the right thing i want to
00:31:03.840
map the constant reference to the file all right so I did this refactor and
00:31:11.120
ship it in 2020 all right as part of Zyber 2.5 and in a moment I got a
00:31:21.640
ticket what the so the problem the problem is that this project
00:31:28.480
has a class that has the hash method redefining it we were talking about redefining name
00:31:36.080
before which kind is kind of common but redefining hash so it it it is not only
00:31:41.679
redefining hash but it's changing the signature because the hash method that you inherit from object has no arguments
00:31:47.600
and this one has one argument it's is not using parenthesis but it is one argument okay and and this has nothing
00:31:54.720
to do with hash codes because this is a special method let me explain what is happening here and why it broke the
00:32:02.360
release so um yes uh I I I ship it immediately a revert of that you know so
00:32:10.000
I have to say that that I have to say that the reporter was very kind and was
00:32:15.200
like look I I acknowledge that this is a little bit weird perhaps okay because
00:32:20.480
it's kind of breaking a contract but uh and and and and he said uh I would be
00:32:26.559
willing to refactor my code but they was like wait a moment technically this is possible technically Ruby does not
00:32:34.000
require that your class module objects are hashable so it is my duty to revert
00:32:39.840
this and think about maybe a different solution so that is what I did i revert immediately and
00:32:47.360
uh that part of the release u let me explain a little bit in case you are not familiar with hash tables what's
00:32:53.919
happening here okay so from object we inherit the hash method and the eql
00:32:59.840
question mark method those methods are directly related to hash tables okay to
00:33:04.880
to it allow it this these methods allow you to use the object implementing those
00:33:10.960
methods as a hash key all Right so the first one generates a hash code uh and
00:33:16.880
the second one says whether two objects represent the same hash key by default
00:33:24.159
uh the implementation that we inherit from object says
00:33:29.440
uh two objects represent the same hash key if they are the same object symbol if you have the same object ID you are
00:33:35.600
the same the same key otherwise you have different keys okay um so um to explain how this works
00:33:44.240
I I thought about the metaphor because when you do a a key look up in a high table and there's kind of two steps
00:33:51.279
there's kind of two lookups happening inside the structure okay there's a f
00:33:56.720
the first one is a fast one and then there's a linear one okay the details of
00:34:02.399
the implementation may may may vary but conceptually this is what is what is happening so the the metaphor is like
00:34:09.040
like the the the key of your room in the hotel okay so uh when when you go to the
00:34:16.639
reception when you enter the hotel and they give you a key
00:34:22.040
okay uh the the rooms in the hotel are organized by clusters are organized okay
00:34:27.919
in the in a high table things are organized also like by clusters they are called normally buckets but it's the
00:34:34.079
same thing okay you have the rooms organized by floor in a hotel so what do
00:34:39.919
you do something very fast you first locate the cluster you first locate the floor and how you do that normally you
00:34:46.960
say we oh the leftmost digit is a four four floor that's quick and you get the
00:34:52.879
elevator to the fourth floor so that's fast and that's the role of the hash code the hash code is an integer okay
00:34:59.119
and it and and there's a mathematical operation that is super fast and gives you to the bucket it leads you to the
00:35:04.400
bucket okay and then when you go to the fourth floor what do you do you you scan
00:35:10.640
okay maybe well we can we can uh ignore signs or everything but you know you you
00:35:16.400
you say okay is this my number no is this my number no that's a linear search and that's what EQL does it is telling
00:35:24.320
you if the both both things are the same key okay so uh what is happening is that
00:35:34.560
uh in a constant reference like this if we compute hash the the array class
00:35:39.599
overrites hash and eql and basically the hash code is compute
00:35:45.599
uh is a function of the hash code of the items to compute the hash code of the
00:35:51.839
first of the first item invokes the hash method on the item and that's the one
00:35:57.040
that it was overridden in that project and it is worse because I cannot this
00:36:04.000
this is out of my control this is the implementation of array so I can I cannot do anything here all right so I
00:36:11.440
reverted and I was like oh man I I am not feeling comfortable with the
00:36:17.599
representation of constant references as constant paths but is the best that I I can I can do right now and I am I know
00:36:25.119
that these things are not hashable so I don't know I parked the problem okay I
00:36:31.119
parketed the problem with time i introduced a proper
00:36:36.160
class to represent constant references at the beginning uh it was everything was simple let's say let's keep it
00:36:42.400
simple at the beginning and then when you when you realize which are the good abstractions and how you want to model
00:36:48.640
your code then you maybe you know evolve the implementation so at some point I introduced the surref the surref is
00:36:55.599
basically the pair okay with the mod and the C name and it has API it has API that is very helpful to express things
00:37:02.800
in a in a in a nice way like I can set values I can set autoloads you know and that's that uh API abstracts me from the
00:37:11.040
uh constants API in modules all right so I did this just to
00:37:17.119
improve the modeling in the gem but um
00:37:22.560
last Christmas or something like that I was like "Oh wait a minute that problem
00:37:28.800
that I had in 2020 with this now I have control about
00:37:34.160
the hash." So if I want to store a Cref in a hash table I can do that i I just need
00:37:41.760
to define EQL question mark and hash all right not like that but with the same
00:37:47.760
trick that we did before now we have a technique to to jump over that that uh
00:37:55.359
overrides so I can go with the same technique i can go fetch the original
00:38:01.200
hash method you know and use it this works this works i was very happy so I I
00:38:08.560
was able to um store C refs in hash tables but I did
00:38:15.200
not ship this yet because then after working this happens you model something
00:38:21.599
and when you find the right the right abstraction sometimes all of a sudden
00:38:26.880
you see things that you couldn't see before or things are possible now that were not before and you have maybe
00:38:32.640
elevated yourself and now you see a little bit better and you you find a solution for something you
00:38:38.000
that happens now there's another API that
00:38:43.040
normally we do not use which is hash compare by identity this is a weird
00:38:49.119
method okay but this talk is about these little
00:38:54.160
unknown methods that are that found their place in zyber what is that
00:39:00.560
exactly what we need compare by identity
00:39:06.880
uh tells when when you when you create a hash and you say compare by identity
00:39:12.800
basically that is ignore the hash method ignore the EQL of
00:39:18.960
the key treat them as if they were objects so it ignores
00:39:25.240
everything and uh if you think about that module class
00:39:31.119
of module objects they are the same thing if they are the same object okay there's no way that the same class or
00:39:37.359
module object is represented into different objects they have this property they are unique since they are
00:39:43.200
unique we can use the original uh implementation in object or the same semantics let's
00:39:49.720
say so that's another way to um solve this problem because now in
00:39:57.839
this case I have a two-level structure two-level hash table that indeed models
00:40:03.520
exactly what is happening in Ruby so I can have for each class and module object the hash table of its
00:40:10.359
constants the point is that here no I can use the module as a hash key
00:40:18.880
i couldn't do this before but now I can why because I have hash compare by
00:40:24.599
identity so this has been another way to work around that so during Christmas and
00:40:31.200
start of January I did these two implementations i had two versions of the gem uh working with these two
00:40:36.680
implementations and then I compare and the second one is way faster okay uh
00:40:42.560
it's it's uh faster than the one with uh with CREF and faster than the original
00:40:48.160
one that I wanted to replace with constant maps so I was very happy very relieved
00:40:55.359
to solve this uh this has uh I maybe the change lock has internal improvements so
00:41:02.079
no nobody's seeing this but I I sleep a little bit better now with this this uh
00:41:07.920
what shipped in February and yes I can have uh cerefs high
00:41:15.720
tables all right we're finishing
00:41:22.839
so a little section about typing typing yes all right
00:41:29.079
so all methods in Zyborg have a type signature all
00:41:35.319
right and um when we think about signatures we normally think about
00:41:41.119
static typing do I like types do I not like types that kind of thing right but
00:41:46.640
my observation is that no matter what no matter if your language is that is
00:41:51.839
statically typed is dynamically typed when you use an API you have to
00:41:57.680
know what you can pass and what what you are going to get back there's no way there's no way around that you get an
00:42:04.560
object you need to know which methods I can I can can it be nil which is the API
00:42:10.240
for this object can I pass a string or or has to be a path name if the name of
00:42:16.800
the argument is file is it is it a file name is it is the file object what what it is i have to know otherwise I cannot
00:42:23.839
program right so to me it's fundamental that we
00:42:30.440
document the types or the duct typing interface or whatever Rubyatic thing is
00:42:37.599
that allows you to program against an API all right so in Zyborg if you open the source code everything is documented
00:42:44.960
public and private because the point is not external documentation the point is that you open the source code and you
00:42:50.800
have context to understand the source code and to maintain the the source code so it is both for users in the case of
00:42:58.400
cyber in the indeed there's no formal API like uh airdoc API generated the API
00:43:06.000
is described in English in the readmi all right I have the types in the rhymi
00:43:11.040
okay so here you can pass uh an string a path name you can pass an array of path
00:43:17.119
names or strings whatever it's it's it's done in English all right but internally this is for internal purposes okay to
00:43:23.599
understand the code and to maintain the goal to help with that so for instance you can see this okay something like
00:43:29.119
that okay and this at the at the at the beginning
00:43:34.560
this has this over the over the years in the project I have used different notations for that and nowadays I have
00:43:42.480
this notation that is taking shape in the community and if you I don't know if
00:43:49.520
you were paying attention there was a slide in the keynote of benicious that was using this notation as well and the
00:43:56.560
thing is that this is taking shape all right uh this is taking shape as an as a
00:44:01.680
as a as a way to represent signatures in Ruby there's tooling already supporting
00:44:07.119
this uh in RBS you can do this in Sorbet you can do this now and
00:44:14.480
um BS code for instance has syntax highlighting nowadays for this so if if you have this signature defined in your
00:44:21.680
source code uh VS Code is going to syntax highlight it okay is not going to
00:44:27.200
be treated as like a a generic uh comment which is very nice so my my
00:44:32.960
message here is this is I believe this is coming we are going to start seeing
00:44:39.200
more of this in the Rails team we have also the idea to at least test the
00:44:44.640
waters a little bit and see if we like you know doing this in the source code we are going to do this as
00:44:51.000
well so this is coming and I believe this is a good thing
00:44:57.200
and I have a last section okay and this like se this last section uh is for
00:45:04.160
everybody but especially for people that are starting programming so um how many of you have
00:45:13.119
been programming for less than one or two years for instance right yeah
00:45:19.800
okay all right so as I said for everybody in the room but especially for
00:45:26.000
you okay section special for you is a section about software
00:45:31.720
errors so the thing is in software we are extremely
00:45:40.079
extremely extremely used to have bugs so every single release of anything
00:45:46.640
has always an item that says bug fixes the most simple iPhone application that
00:45:52.720
you can think there's a new release chances are that there's an item that says back fixes in companies your
00:46:00.480
exception tracker is normally very be very busy all right and if we are new to
00:46:06.640
programming and we are receiving all these inputs maybe we get to the conclusion
00:46:14.240
that that this is the way it is there's no way around that you know and that you have to just
00:46:21.040
accept it okay so in the 90s in in a
00:46:26.079
forum in Usenet for those of you that know what Usenet was I saw this sentence tech which is a
00:46:35.760
type setting system written by Donald Nuth used for scientific papers and math
00:46:41.839
extremely complex software the sentence was T is considered to be BF free that
00:46:46.880
was in the 90s okay and that was a revelation for me and an inspiration
00:46:52.200
because I was in this mindset of yes box uh I don't know there's no way around
00:46:57.680
that and all of a sudden I see oh this is
00:47:03.880
possible and this is the message that they want to pass to you this is
00:47:09.119
possible so in our case Zyber has has had about
00:47:15.760
60 releases is the race autoloader since version six six is being used by 700
00:47:22.640
gems more or less it has half a million daily downloads and more than 4 uh 100 million
00:47:30.880
uh so sorry yes more uh 400 million uh total loads this includes CI and local
00:47:38.480
installations and deployments and and whatnot so the the number is not that important but you could say it's used
00:47:45.760
yeah you can all right so Zyber routinely has no issues
00:47:54.520
routinally all right sometimes you get one okay i I just show one with a hash
00:48:00.880
thing okay occasionally you have one but the the the the norm the norm is that
00:48:06.160
there's no issues there's no known errors it doesn't mean it's bug free but there's no known errors
00:48:13.880
okay and we have more examples like this in the Ruby community for instance um
00:48:20.160
SQL from Jeremy Evans buckfree no known errors let's say that
00:48:25.599
way action cable from Vladimir dement no no
00:48:30.800
errors as well empty issue tracker okay for me the the good issue tracker is
00:48:35.839
empty has it has to be empty all right your exception tracker should be empty
00:48:41.440
so this is not something easy especially if you are on tight deadlines especially
00:48:48.720
if you do not have good coverage especially if you are inheriting a code base that is difficult to maintain i am
00:48:55.280
not saying this is easy i am saying this is possible so that's the message for you for those of you that are starting
00:49:02.800
the message will be do not take box for granted we I I have shown you uh a few
00:49:10.960
models a few references that might inspire you all right do not take them
00:49:16.079
for granted so my message is that uh I encourage you to think
00:49:22.680
independently and to pursue your own excellence and your own standards that's
00:49:28.559
it all right thank you