 My name is Mero Chavez. I work for the development shop. You have the mall worker? The name of the company is Chara.io. And what I'm going to talk today is about rediscovering active record. So probably we are all familiar with active record. When I came to Rails eight years ago, the first thing that I hear from the people is what was that active record was magic. So let's get started. We just create or define a class. How I started. That's pretty much what we needed in order to make it work professionally about two years ago. And pretty much the first we got into that. I just find out that actually there is no magic in active record. Active record is doing pretty cool things for us. It's hiding a lot of the complexity. And so that implies to have a model be connected to a table in the database. But at the end, there is no magic at all. And what I'm trying to do here is talk. It's just to do some exploration and go to the main actions that active record is doing for us in order to transform the query into an object. So let's start with that. Any of the actions that provide function. Probably we are all familiar with this line of code. It has like a Russian dollar, right? We are basically just an app using a generator in order to create a model. But we are going to end up with something like this. Again, it's just a fast operation. It doesn't have any internet. But still, if I open up the console, I can run this query where I'm asking active record to go to the database and pull one of the records with the ID one. And what we see below is the actual response that we may be expecting on the screen. In this case, I have an ID, I have a name, and email address. And obviously, we're going to have the graded and updated app timestamps. A nice UI for dealing with standard code operations. You have on the top record doing this. How can active record come from this into being able to execute my query? What is it doing? How they're doing all their magic and reading. So the first thing that we need to understand is that active record needs to do some kind of introspection. And with introspection, the first thing that active record needs to perform is to ask the database, what is the table schema? Once that we have this table schema, then we can do a lot of things. So the first thing that we load JavaScript or CSS, a model in our application, and we try to do any kind of operations like a query, the first thing that is going to happen is that active record is going to try to load this schema. Basically, what it's doing, it's calling this method. If we fire up the console, we just use a model and call these attribute types. What we are gonna find out is that this method, it's gonna fire something inside of the active record code which is called load schema. No, we cannot build a Rails application. This method is delegating the call to the actual connection, the actual database connection to ask what is the schema for this specific model. The model is not here. The connection is laying on the schema cache. So it means that every time, or at least the first time that we try to use a model. That's pretty straightforward. Active record is gonna go in there, pull the table schema, made a copy of that, cache it, and leave it ready for us for the next calls to this model. So it means that only the first time for each model, we're gonna make this kind of call. Also, this means that the connection is keeping a hash, and this hash, it contains all the attributes, all the schema for this specific model. And we can see, for example, in this piece of code, we are expecting, we're asking the hash to give us the schema for a specific table, if there is nothing in there, that's where the connection actually issue a query to the database. It will depend on each database, how the query's gonna look like, but at the end, we're gonna get all the information that we need to understand what is the schema for our model. Say it is not. So, yeah, we can use all the Rails application. What we are getting, if we call directly the schema cache column hash, for example, for the table user, we're gonna get this specific hash. We have, the keys are gonna be the columns of the table, and the value, it's gonna be an object that it's gonna have a lot of information, but probably some of the most important piece of data that we are getting here is the attribute type. We can see here that the database is an integer, but also we're gonna have default values and any other important information that we can use inside of ActiveRecord. Now that we have this understanding, the loaded schema method also needs to give us a little more information for the Ruby side. Basically what we are gonna need is gonna need a cache type. And a cache type is something that will allow us to convert whatever the database is telling us that it's gonna be the data type that we are storing the database, and what the data type is gonna become on the Ruby side. Obviously for integer, probably this is very simple. I mean, if we have a number, we're gonna have, if we have an integer in the database, probably we're gonna have an integer on the Ruby side, but for things like, for example, timestamps, this is gonna be a different story. We're gonna need something that will help us to make the cache between the original data type into a Ruby type. Here's what I'm gonna do. What is happening inside of ActiveRecord is that it does implement this active model type value. This is a base class that helps to define how ActiveRecord is gonna make this casting between database types into Ruby types. If we inspect the ActiveRecord source code, we're gonna find out that we have an active model type integer, active model type string, active model type date, and some other objects that will help us to create this cast. And this is something new in Rails 5. Well, actually, it is implemented in Rails. So it looks like it actually called our land up. But the API is private. It's not available for us, but in Rails 5, we can actually create our own types. Like for example, imagine that we have a field in the database where we are storing an edge store data in there, and then we want to pull the data into our Rails application. But instead of getting a hash, we wanted to convert that into some sort of object. And this object doesn't really need to be an active model object. It can be pretty much everything, or probably we can convert that into an extract or something like that. So we can actually define our own data types for Rails. And the way that we can do that is we need to implement at least three methods from this type value class. The first one is serialize. This is the method that we are gonna tell Rails or ActiveRecord how we are gonna transform wherever we are getting from the database into a Ruby object. When the screen's not right in front of you. The second one is cast. This one is being used for user input. So for example, it says that we have a form and in the form we have an input field. And this input field can accept a number. When the form posts that information back to Rails, we need a way to get the number and convert it into the final type that is going to be stored in the database. And finally, we need to implement the serialize method. The serialize method is gonna dictate how the Ruby type data is gonna be transformed into the database type. So as I said, this is part of the attributes API. Without using a model. At this point, now that we were able to pull the database schema. Now, I just wanted to do one more thing which will hopefully provide a casting type for ActiveRecord. It's still pretty boring, it's just serving a static string. Our model will be able to understand what's that ENV that's being passed in. It's on the schema. So I'm gonna turn that into JSON. If we call these attributes types, we can see here the response. So look at that. So what now? We know that we can understand the schema, but we cannot still get to this point where we can actually run the query. The HTTP request, right? You see the ActiveRecord needs to perform is to create a query for us. Like here, it's like the cookie information. ActiveRecord is smart enough to try to cache. Cache query objects for us. Let me explain why. Find my mouse, here we go. When we execute a query, for example, this dot find and the number. What it's gonna do, it's gonna try to find if that statement has been executed before. Provide an interface between. If it is, obviously, we are gonna just pull that query and we are gonna perform whatever action it needs to be done, but if not, then our find method, it's gonna be converted into a query. Sorry, it takes an environment hash. So we can see here in the block. That's the query. That's how the find, dot find number, dot find ID, is being converted into a query. But there's something here that we need to pay attention is that in the where clause, instead of passing the ID of the object that I'm looking for, Rails or ActiveRecord is doing something, params.py, and we're gonna see what it means in a minute. Taking list of things that match. So we don't have the query, and it means that we're gonna have to create a new one. If we go to the statement cache file, we're gonna have to find this method. And this is the part that takes care of creating the query for us. Like when you say render, the first thing that we are gonna need is to convert the dot-word into RL's ASD. And that's what the first line of the create method is doing. This block.call is actually executing the where clause that we see before. And this where clause is being converted into this RL3 where we have this node, which is quality. We have the attribute, which is the attribute ID. And then on the right side, we have something called bind param. This is kind of a placeholder, where maybe we're gonna replace that with the ID of the object that we are looking for. And actually if we see the other structure that we have on the left, we see at the end that we have a block, which reference to the ID, but instead of saying that it's an integer or displaying the value, what it's actually saying is that this is a substitute. This is something that we are gonna to replace at some point. The next part that we need to, or that the create method is doing here, is it's creating a bind map. The bind map is basically setting up what are the params that we are gonna pass to the query. The first one on the right is the value for the limit clause because we are doing dot find, and we are actually asking for just one record. And the one on the left is the definition just for the query attribute in this case, is the ID field of the array. The next thing that it's gonna happen here is that we're gonna create this query builder of the RL-AST, and this is what actually is gonna construct the SQL for us. In this case, it is using the specific database adapter in order to create the right query for the right database. So in my case, I was using Postgres, if we were using MySQL or something else, the query probably will be a little bit different. But here, what we can see in the query that this query builder is screened for us is that the ID and the limit has these question marks, which means that those are placeholders that we are gonna replace at some point. And doing this will allow us also to cache the query on the rail size, but also we are gonna keep the chance on the database which means to cache the query, the execution plan for the query in the database. That's like a wide, like normal, right? So yes. Any kind of rack? So yes, you made up with me up to this point. Well, the model knows what is the schema. We know that we needed to build a query. We have a query, so what is next? We're almost done. But still not there. Many of you are. You are actually running. At this point, we are ready to execute the query. What is going to happen is that now that we have the statement, we're gonna be able to call this method execute. And the method execute, as you can see, one of the patterns is the actual ID that we are trying to... You can mount Gap or Key. The record that we're trying to pull from the database. And well, we also pass in the connection. The execute method will do a couple things here. The first step, it's gonna actually perform the binds. Do you remember that in the previous steps, we have a binder and we have two attributes in there. The first one was the limit clause. And the second one was for the ID. But at that point, the ID was marked as a substitute. At this point, we are actually replacing the substitute. We are replacing the value with the real ID of the record that we are trying to pull here. The Rails engine. So what's the thing? What we are asking in this step is to actually give us the SQL statement, the string that we are gonna send to the database. We see that we still have the placeholders. What are Rails engines then? And once that we are ready, what Active Record is gonna do is just execute this find by SQL, where we are actually going to pass the SQL query, the bind values, which are the values that at the end are gonna be sent to the database for query. And what we are gonna get is, at some point, is a result set. This result set, it's gonna be something like this. It's just an object that has the columns and the rows. And for the rows, it's an array of arrays. So on the last step, what we are gonna perform is we are gonna execute a map of this result set. And the instantiate method is the one that is gonna create the instances for each one of the records that we are pulling off the database. You know, kind of, but would feel too distant from reality. Just to feel like we know we went like this. Let's try it. The instantiate method, those a few things. The first thing is that it needs to allocate the object, like actually create the object of the user model. And then it needs to initialize the attributes. And this is an important step. Actually, the code for initializing the attributes is the one that you are seeing here. The first thing that we, that active record needs to perform is to define all the accessors and the helper methods for the app. This is the first time that we use this model. So it doesn't have like a dot name or dot email. And this is where those methods are gonna be defined. Inside of the model, there is a flag that says if all the attributes has been initialized or not, if not, then there is something that runs and create all these methods and all the query methods also. The logic for this is if you want to look into the source codes is the attribute methods. Actually, the logic is very dense and probably it needs a talk by its own because it's active records performing a lot of work in there. But anyhow, now that we have all the attributes, the next thing that we need to set for the model is to create this lazy attribute hash. This lazy attribute hash is every time that we call like a user dot attributes, actually we are delegating that call into this class. In this class is the one that knows what is the data that our model is containing or that the object is holding on. It's fine, don't worry about it. If we inspect this lazy attribute hash, we're gonna find out that it has two main variables here. The first one is the types. Do you remember at some point I told you that once that we get the schema, we need a cast type and this cast type is the way that we're allowed to convert the data from the database into a Ruby type. So here we have this hash where we can see each of the attributes, what is the cast type that is gonna be used and also we have the values. The values is where we are actually holding the data as it comes from the database. There is no transformation here, there is nothing that we haven't made to the data yet. It's just the raw data that we get from the database. The name of this class, lazy attribute hash, is, it has a meaning. Okay, we'll work on that. Let's say that our model has 10 attributes and we do user dot find one and we get the model. So if we wanted to cast all the data for the 10 attributes, just as soon as we get the data, obviously we're gonna spend time over there just doing the casting in order to prepare the data to be used by us. But for example, what lazy attribute hash is doing is that it is delaying that. If we just do user dot find and number one, we're gonna get the model, but no casting is going to be performed. Until we do something like a user dot name and we try to pull the name, then that's when the lazy attribute hash is gonna perform the casting. This is the same for the rest of the attributes. So once that we do something like a user dot ID, as you can see there is another variable inside of the lazy attribute hash, which is the delaying hash. And this one is actually containing the value that it comes as a raw value from the database and the final value as it was casted. For example, if this were the example of the timestamp, we're gonna see on the value before cast that it was just in a string. And the value will be the actual Ruby type that represents the days. Things go wrong because they during. Yes, it's working lazy. So finally, after going through all these hoops, Active Record was able to perform the query that I asked for and I got the expected result. So as you can see, there is a lot of work that is involved in order to go from just class, user declaration to actually having Active Record perform something for us. So what we learned here, the first thing again is that there is no magic. What Active Record is doing actually a pretty cool thing. Obviously what I show you here were mostly the highlights, but following the code is not that hard. This is actually pretty easy. To just pick up one of the query and try to follow up whatever Active Record is doing underneath. Another thing that I learned, what I was doing this is that Active Record is gonna try to cache and be as lazy as possible. Every time that we work with the models, if there is a work that it doesn't need to be performed like the right way, Active Record is gonna just put it in a hole and then do something else until we actually require that. And if there is a chance for Active Record to actually cache whatever we are doing into the different operations, then Active Record is gonna try to cache that information for us. And well, as I said, following the code is not that hard. We don't really need to go deep into understanding how all the things work in the framework. But obviously, we need to have some sort of a curiosity with the tools that we use every day just to get a great idea on what's there. What is going on, how things work. I got interested in this because I was writing a blog post probably two weeks before the first beta one of Rails 5 hits. So I was going through all the different pull requests on GitHub and I find out a lot of things that were happening inside of Active Record. And that's how I just started off with the curiosity to see what is happening inside of Active Record every time that we run the query. Okay. So the first thing that you'll notice. Yeah, for me. Thank you. Exciting for a gem. Of course, it is a gem. We do have our gem spec. So this is kind of like the... I don't know if you have any questions. Rails engines, is that you? Other places. Oh, sorry. One more thing to mention about that gem spec, that's kind of important. Well, there is a... With gems? Oh, yes. If there is any other resources other than just going directly to the code, if you want to learn or to understand what is going with Active Record. Sure, there is an IRC, there are Google groups, also going directly to the GitHub. And we're gonna be working with the gem spec as we go on. If you look in lib, you'll see the engine.l back, and everything's gonna be named as a module, which is why you have a name space. Everything in this engine is going to be namespaced, and we'll see how that plays out. And because there's an engine, it's for Rails engine, that's the thing that tells Rails, like, hey, treat this as an engine. There's a raps.rb, which you'll see. It's not on a real... It's in your engine, right? We had our own engine. No, sorry. I never been in that specific situation. When these are request, it'd be... Yes, sorry. Level, like, I don't have time. I'm, like, my own engine. So we're gonna work with the file a lot. Thank you. And... You know, I... Thank you. In the app property.