define_method - Metaprogramming in Ruby

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
everybody i wanted to talk about define method in ruby which allows you to dynamically add new methods to objects so the place that you might have seen this before if you work with rails is with the enum helper or the enum helper so if you have like maybe have a model that's called like event or something and it tracks your your web hook notifications and maybe that inherits from some active record base we'll just say model base for now um that's not a real thing but we'll go implement that and then you you might have run across this enum helper which itself takes in uh the name of the field so in this case it might be like status and that would point to either i think it's either a dictionary where the um the keys point to the value in the database or it might also just be an array here where the index into the arrays with which value is actually stored in the underlying database but i wanted to talk through how you might implement this with enum or i'm sorry with uh with define method so if we have some status here and let's say that it's uh it takes in you know pending and then success and then failed or something right these might be common statuses that you would have on some event and then ideally what we can do is we can call like um event is equal to event.new and then we could say something like event.status is equal to pending so we could set the status this way and we could also print out the event dot status here and that would print out pending so this is kind of where we want to get to initially and so if we ran this right now this is going to fail because of a couple of reasons for the first is that we don't actually have a model base defined yet and the second is that status isn't a method on event so let's first define model base class model base and then we'll run this again we're going to just use like sort of error driven development here edd so undefined method enum for event class so let's add that into the model base in case we wanted to use this enum thing for other models so we're going to say we're going to define a class method because here when we're when we're calling the enum method inside of the class body this is not inside of an instance this is at this is a class method on event and we can define it in the model base because where our event is going to inherit from model based so we say self dot enum this is where we define we're going to define what enum accepts and i'm going to have this except in just like a a bag of keyword arguments and so initially what we can do let's actually just run this again and we'll see that now we're f we're failing because uh there's no method defined status equal so there's no setter there's no setter method on event right so status equals but let's look at just what keyword arguments is and it should just be this dictionary here with one key being status and one value being this list of symbols so if we print out keyword key keyword arguments and then run this again we don't actually get any because let's see okay here we go status pending success failed so this is the dictionary that comes in so i want to pull off status as um as one of the or as the values we kind of want to loop over the keyword arguments in case for some reason there was you know multiple of these enums passed in maybe you have like status and you have some other thing that has like values x and y theoretically you could support multiple enums for now we're just going to use one and i'm going to loop over the keyword arguments with each do key and val okay so key is going to be status and the first thing we want to do is use define method to define a new method on on the instance called status equal so we want to define the setter method so we can say define method here define method and then we want to pass in status equals and so status is in the key and then we just add an equal sign after it to make it a setter method and technically this block when you call a setter method like this um you're actually passing an argument and the argument is whatever you're whatever you're passing on the right-hand side so we're gonna set the value or pass an argument to our block called like um you know v right that's the value that we want to actually set and then here what i want to do is create a new instance variable with the same name so instance variable set now you might in practice want to call this something special so it doesn't collide with other instance variables so you might set it to like underscore underscore and then the name of your key or some such i'm just going to keep it simple and name the instance variable the same as the key so that event will actually have like an instance variable called status and then we'll pass v as the value so um when we call like when we call this enum method um passing in status that will dynamically define a new status equals method and when you call status equals it will set the value of an instance variable called at status to the value on the right hand side so let's let's run this and see how that works okay so we have no output yet um that's fine what i want to do is let's p event and let's see what we get here okay so we do have we have an event object and you can see in the output of the event that it has this instance variable called status and its value is pending and if we wanted to change this to like success we could do so and then if we ran this you would see the status is now success so one thing we could do to improve our setter is to is to check to make sure that the values we're trying to set are in the valid set of statuses so we could say something like if um uh kwarg's at key actually this is going to be values right so if we if we just say like if values dot include v then set it else raise like i don't know some no uh no not a valid status or some such and then we can put in which status they tried to set okay so if we run this again we see the same thing because success is a valid status what if we try to set it to like blob or something if we run this we should get an error and we do we see an error that says no valid status blob it's a runtime error okay so this is how we define the setter while we're looping over this we might as well also write a method for retrieving the status so that we can call this event.status so right now if we try to run this we're well blob isn't valid so let's say pending um and then if we run this we see uh there is undefined method status did you mean status equal so we haven't actually defined the status getter so we can do that like this and we can just pass in um in fact we can just pass the key here and then we actually this so a getter method does not accept any arguments and uh we don't need to raise any exceptions or anything what we're going to do is we're going to say instance variable get and then we're going to pass in the name of that instance variable again that's going to match this name here so if we run this now we should see our status coming out instance variable get define method um we're missing end of input what's going on here um did i copy too many ends uh oh i'm missing a do okay all right so let's run this again okay and we see pending so that's that's great we were getting back the status that we just set right so we set it we set the status to pending here and now we're reading that back so if we're looking here we're looking at the documentation for active record um in the active record api for enum right you can say conversation.active with a bang and that will set the status to active you can say conversation.action active question mark and that will return true if the status was set to active and it will return false if it was not set to set to active so let's go look at how we might implement these so let's do uh first let's implement the um the bang method which will call the setter with that status so for each of the values right so active is one of the values archived as one of the values so for each of the values we need to also dynamically add a couple methods we're going to add the bang method and the question mark method so here we're going to say define method and we'll say you know key no i'm sorry um actually we need to loop over the values so values.each do v and for each value we want to define two methods we want to define a a method for this for the bang so define method and this is going to be called uh v with a with an exclamation point at the end and this will result in calling the setter method so um we can either call the setter method directly by saying like self.send and we we talked about sending in a previous video so we can say self.send key equals and the value here and that should set our value equal to that so let's try this here let's say event dot pending bang and then we'll just print out event status so if we run this what do we get and we get back pending so calling pending bang updated the status let's also let's just make another call after pending bang and we'll say success bang and that should result in having it updated to success so that looks like it's working um alternatively like instead of calling send here you might have also just called the instance variable get or i'm sorry the instance variable set the reason why i wanted to use the send method is if for some reason you wanted to override the that setter you could do so in the class method or i'm sorry in the in the yeah in the subclass and by calling uh pending bang and setting the status you could take some other actions that says like you know transition some other state or you know count up how many things are pending or some such so this is a little bit more flexible um all right so let's also define the question mark method right so here we're going to say the question mark and what v question mark does is it it calls the getter for success and it says give me back the value for the key and if that is equal to v then it's true if it's not equal to v then it's false so let's see uh so we called pending now let's call uh you know let's puts uh event.success question mark and it should not be so this should print false and then after that we'll print pending question mark so we should print false and then we should print true because the the status when we're using question mark we're asking is the status pending or is this data success and the first one we're checking it's false because it's not success it's true because it is pending right and so we're able to sort of like check and see if that is updated so um this is one way that you might use the define method uh tool to do some meta programming in ruby okay all right until next time i'll see you later
Info
Channel: CJ Avilla
Views: 444
Rating: undefined out of 5
Keywords: metaprogramming in ruby, metaprogramming, ruby, define_method, rails, ruby on rails, ruby metaprogramming, ruby define_method, Define_method ruby
Id: I0itVuoprAY
Channel Id: undefined
Length: 12min 45sec (765 seconds)
Published: Fri Jan 22 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.