Shannon Skipper (havenwood) has been a Rubyist since 1.9.3 and helps maintain chruby, ruby-install, RVM and an odd assortment of gems. Shannon lives in Los Angeles is and an Engineer and Evangelist at Square.View the profile
About the talk
RailsConf 2019 - Learn to Make an API-Backed Model with Square’s Ruby SDK by Shannon Skipper
This is a sponsored talk by Square.
Active Model is flexible enough to provide a model interface to APIs, not just databases! We’ll look at how to go beyond just including ActiveModel::Model to implement a rich set of Active Model features, including the recently-added Rails 5 Attributes API. How does this compare to Active Resource? What does it look like in your controller? We’ll answer these questions by exploring an example that wraps Square’s Customers API in a full-featured Rails model.
Okay, I almost had to talk. So I'm Shannon Skipper. I'm here talking about backing a model with an API instead of a database. So let's get started. First off. This is an idea that is as old as rails active resource is something that ships onto the rails repository and never was made part of rails. But what it does is let you point a model at a rails app. That's remote. It could be in a local hospital cost to be like over the web and it will then use the expected rails. Json API to over-the-wire
serialize and deserialize and work as expected. So you can hear do customer. New customer. But it's the downside is that it has to be a rails Json API and that was an edge case enough that it just wasn't flexible enough to get merged into a real. Man, because not enough people use it. There are plenty of people who use this in fraud. It's really useful. If you just need a direct connection to another realtor base. So what if you want to go to the basics and make something that's more flexible than active resource because you have an API, but it's not a real TPI. It's not a conventional reels
Json. So if we look at the model where we can start with the model is just a customer class. This really doesn't do anything. So what do the active model docs have to say about what we can add we have at 3 to call backs conversions dirty Etc. So one single include will actually get you a pretty large swap of this Factor model model. So when you include actor model model, this is code from that module it will actually automatically included the attribute assignments Valley validations conversion naming and translations. So why
does it do that? It is for action pack an extra Beauty. So when you're using your model in your view it knows about the things that rails needs to know about the model in order to have the automatic parts work. So it does not include the attributes callbacks dirty, Json serialization sterilizers or the one test to look at those individually. So let's just get started looking at one of these first will do attributes. So we went on the attributes API to a bear Ruby class. What does that look like and attributes API something that's been added in recent Ralph.
This is a real spies Edition. I'm actually at active model are active active record attributes rs52 was when active model attributes of that it so it's brand-new listen to real sixes out. So this is how you can do in rails 5 to you can just take a bear Ruby class customer include active model attributes and go ahead and set your attributes. These are typed attributes. Will see that the name is a string the manager. So if we go ahead and use this class but no other code in it just by adding the attributes API. We now
Ken us at our age with a sweater to a string 42 and it's going to return when we do use the gator the integer for you to be under the hood. It's going to do that koreshan. So let's go ahead and I'm going to show some more examples of the attributes API here in this code will bigger. So with I'm going to use for an example squares a customer API. It's one of many square apis around supporting businesses. So we have customers they have preferences creation time their birthday. And so let's look at some of these attributes. So we have the ID and creation Source bowstrings.
We have these Day times would come out of the box hash in this array of hashes are actually defined by me so you can take the types that rails gives you out of the box. You can also create your own type. So I'm also to note you can set a default which is the default placeholder. So for all of these on Springfield attributes, I just went through all these fields and they're just all strings. We can easily I'm change them to have like a default of an empty string just like that. so let's take a look real quick at
those custom attributes. So that would be in the config initializers and we'll just I created a types. RB file which is the convention for where you put your types and here I have a halfpipe and all you have to Define is the caffcast method taking the value and doing operation you want to do so here. I'm just taking a Hash Pipe is just takes the keys and make some symbols and then a array of hashes type. It erases through an array and maps to symbolize keys to this is just an example of how you can create your own type is that we look back in the
customers file. You can see that I just put the pipe array of hashes. We've defined it. So it's now going to do the coercion when we could affect the record. Okay. So that the attributes API it's really powerful. It gives you date of database like type Coors and that using an arbitrary source for your data. So let's look at the next on the list. So this one is one that you also have to implement yourself. It's not automatic but it is part of the actor model model. I'm included so we get it here out of the box. Basically the parts you
do here is you define the attribute. I want you define the model call back that you want to have. So I'm to find update and save and final callbacks has just given to you by including this module. So let's go down to the update and save implementation in this database. So what you do is you provide a run call backs and then the one you want. So it's saving saved bang will do a run call back and then like for update an updated. We have a run call back. What does is it going to be is the gut of these methods goes inside of the flock and if you want to put up. 4 save
an after update they will know that these are the parts that you if it's the middle, so it's before this block and it's after this block that the bits will run but the return value of this block will persist. So this is basically how you implement your own active record style before and after call back. So let's go back to the slides. And another this is a simple one. That's just conversion. I don't think you can really see here. But the second version is highlighted. So conversion. All you do is include active conversion and it
provides to partial path. So this is part of what makes action pack an action view just work. Why does my 18 ends that's really where the meat of the thing is after model dirty. This is a really interesting one another one that you don't just get out of the box. You have to implement yourself. So let's take a quick look at that. So here we've included I could model dirty and then that out of the box is not going to really do anything. We need to Define field will change
and so active model dirty is going to look at all of your attributes and create a method Missing Method that ends with will change when you call that method it's telling rails should I get the value in the value of this afternoon has changed and so Wales now knows this is this value is not what it was before. So the other bit that you have to implement is Changes and that will be I just put this in a helper method called persist where in rails record knows if it's been persisted
for this model that we've made it there is nothing that knows if resistance is an instance variable when it persisted if that's true when you might say delete it it's going to separate ball this triggers to that's for the triggers to active model telling it whatever this model was changed to these changes have been persisted in this case to an API normally with that can record to so this is pretty neat because with only these two methods calls we get a hole in her face. So let me do a little Kodomo.
So if we do a special customer from the API, and hopefully the internet is working here cuz this is from squares API. So so this is a a customer object is a real real model is API and if we go ahead and change about you, like let's take the given name and will give it my first name. So Shannon and let's take the company name is API at work company name all set. To square. See you. Changes just because of this knows that the given name went from a me to Shannon and the company name went from an empty string. Just
wear if we now Dulce. Save. This is going to do an update against the API. We can now check the changes. I think there are none because the customer has not changed interesting Lee we can also let slip set the set the family name to my last name Skipper so we can also do Is it restore and we can do attributes to restore all attributes, but we can also just restore family name directly. And so now if we check it on the family name is back to hey, I'm so this is active active model dirty, including octal bottle dirty and staying when something is changed
real provides all these helpers out of the box. I didn't even cover all of them, but it gives you just like what you expected. Okay, so back to the slide. So another active model module. That's interesting is the serial isers. This is also one that you don't get out of the box. You have to explicitly include but it just works. There's nothing else that you need to do to set it up. So active model serialize without the Jason on the end. I miss something at the Json one includes. So if you want both you can just include this one and you get a can
bottle sterilizers and optimal serialize. Json serialize wrist do it. Basically lets you convert your model into a half actually really useful and under the hood things can use to your lies will hack. I'm at the hook for this by adding the Jason extension as well. You get two days on just like you would expect from a real smile. Okay, what's going on? So validations? This is useful one. Let's pull out to Kodomo. So I've only had one validation here because the API actually has all its own
validations. But anything we want to handle before we even make API call can be done here. If I just have a valid email address by including actor model model you get validation II model model really is a drunk tour of these that is a useful subset. They're sort of Auto implemented so you can just work with it. Just like you would expect all the allegations just work in traditional rail fashion so we can show those off here. Like let's say we try to set the email address to just stop playing at fine and we can check that this
is not valid. And so if we try to save it it's going to or State Bank and its foundation are validation work just as you would expect and by including acting model model you get them out of box. So another thing that you get by including I can model model is naming and translations. So active model naming you extend naming and you can instead include actor model model. It will include extend it and this gives you model name name, which again is used by
action action View and it also gives you a model named human again. This is really only useful for the i18n internationalisation elements of things. It's not doing a whole lot here. But as you at hooking I 18 and he's become important. So one other helper that you get from active model model is the translations module which you can extend as well and it gives you the human attribute name, which is the similar to the naming module except. It's for attributes instead of for the module. Again, this is really about internationalisation.
So the final thing that the actor model implementation has in it is the one test and that's actually really awesome this when you don't actually include in your class including your tests, but let's say we have this isn't our model test. We include back tomorrow. And then the one thing that you're supposed to do is set the model instance variable and the test we'll take it from there. So if you sent this one instance variable, you get six runs 35 instructions and it shows you that you have one working model. It passes the rails like models
including I can model model you should get here. So let's try these out I just for this talk created two models. So we should if I my math is Right have something like twelve assertions and I've gotten rid of the other test for this. So let's run Drake and the real ones that was six. 1035 assertion teach us a nice way to very quickly get some sanity check that your implementation is a full active Mall information. He doesn't check any of the active record helpers,
which we can look at next but it does give you a good basic groundwork. So other things that you might want to look at when implementing your own model is pretty print and inspect because these are not going to be given to you out of the box by rails and it's going to be a real difference of differentiating factor between what your mama look like and what a real real model looks like a real so pretty print. This is in before it actually is now that's a recent Ruby required by default. So you don't even have to require PPE
for a while, but let's take a look at how you can Implement that. So in this model down at the bottom, I've implemented these to help her methods so pretty print and it has like more details here and then inspect and this is just a tighter on definition. So for pretty friends were taking an object address Group, which is just you can look in them pretty print Doc's they're basically different ways to do these layouts and then I'm taking the immutable fields and the the regular fields and
then differentiating those slightly by style and color. This is just a convention. I came up with you because in a rails regular real estate of Base, you can change all of values. Where is if you're going through an API there may not be a way to change certain values that are readable but not writable. So let's take a look in the rebel at what that looks like. So if we again do like customer. Knew, this was a brand new customer and you'll see hear the purple fields in this case. Our API knowledge that we can't change.
The gray fields are our attributes, which we can absolutely change. So let's try on changing an attribute. And we see my saving and now has fetched these immutable Fields because under the hood creating a new customer or did nothing with the API. It's purely a rails model on save it actually under the hood did a create customer through the API if we now take that customer and change something. The next save will do an update on the bed. So under the hood here on on customer. New nothing happened with the FBI on initial save a creation happened with
the API on the subsequent save an update happened with the FBI and the pretty printing throughout is just to give this nicer look at the customer object for working for comparison. If we look at the inspected version, let's like print the customer. So here still return the pretty version but above the pretty version you can see the inspector version inspect version looks less like a rails model anymore. Just like a standard Ruby class. We actually here has the customer instance and then I've
recreated the object ID which is something that you can do to match what that other kid. He really isn't Ruby will take a look at that in a second. And then I've added the the immutable and regular fields to this inspect. So I really like the free print. I mean, I think that these colors are fantastic. I think that the pretty print style is really worth doing but definitely if you're doing your own model or even with service objects consider implementing pretty print for them to step it up to some the rails level of console interaction. I'm so pretty printable invitation here is
mostly just about printing those attributes. The inspect is similarly online on this line is about printing attributes the next line. Let's take a look in a little more detail and this is just about showing the object ID that Ruby would anyways so we can expect when you do an object in Ruby you get this text gibberish after after the object. So we have hash open brace the class Name colon, and then we get the text number. So what is that hex number? It is
actually a relation to the object ID of the class. So in this case, we have a 2.1 billion if we left shift a bit and then if that's the same operation is X and I to we get 4.3 million roughly so that hex number there is actually the hex representation of 4.3 billion and it's the memory location of this object in Ruby. so if you want to implements like additional things that you you're getting back like I'm the box here with a customer. We we get back something almost identical to
that object. Knew where we would just be our customer class like regular a colon and this memory address if we want to add the attributes on we're going to lose that first bit. And so this is a way to recreate that first bit. They are expecting so you can addendum additional information. So let's look at that one more time as a full string. Basically inspector is returning the class. This is going to be customer. And then it's going to take that object ID of this particular instance left shift it by a
bit and then format that this is just using text formatter. So I'm just saying give me a zero at the next 10 18 value places. So by implementing pretty print and expect me not have a nice user-friendly Rebel interface for a custom glass. Okay. So in this demo I am using a new ruby gems where has a ruby gem called Square connect Square underscore connect. You can install that was Kim install where can I buy? We also are creating a brand new ruby gem and then looking at ways to do things like
generate or have a real engine around models. So you can use our platform-as-a-service and don't have to do all this. Should I make a service object. Should I put this in my controllers will help give me gets out of the views and have some of this work already done. So let me show off real quick some of the things that you can do with us, so See I have rails running here so we can go over to a browser and get the internet is working. So this is a controller that was generated
for that model and all them. The model has a bunch of weird Parts in it and busy work you can use it from a controller and from of you as though it was a day trip database backed her bottle so we can hear I just I'm not a designer. So this is a free basic and materialize theme but we can click on these individual records like by clicking that it under the hood is like for rails rails. If you mention it just looks like a regular s controller under the hood in dispatch this from the API if we click to edit we can change the name it save
a live update under the hood. We can delete a record. Return to hatch a nation across multiple cursor and request and basically we have a working model that we can use to back around out. So let's take a look really quick. Then at the code that makes it possible. So the model that we've gone through we've gone through how we included attributes. We included dirty to serialize wheelers and then this way there. I've also extended innumerable and this is we just have to implement each
and we thereby can get it eration through like all of our customers as well as helpers like first and then on this one line, I'm using squares new Ruby SDK, which will be releasing Polly by the time this video is published. But here we just give it our token and pull up the customers. So it's really this is mostly about defining the rails model rather than like using API API is really just a couple one-liners. I'm in here, but it's where all day to come from. So here we're just doing attributes to be disgusted
disgust and we've added this persisted. So you can check the persistent State. This is just an additional helpers for active record like niceties we can do an attribute method suffix. And basically this means if we have a customer birthday, for example, you can do customer birthday? And that's going to internally just check public send a birthday present so we can also do regular assignments select. Let's take a look at that. And then I have to find all create
update delete in other just active record type helpers. Let's take a look at creation. So we can I showed already like creating a customer and then you can subsequently set values. You can also create the customer with. Values upfront and passed them as an object. You can do the regular Setters. You can do like update attributes. Basically, it works as anticipated and then let's go over to the controller that backs this model for the demo. We just looked at I just have controllers for the two models regenerated and here I just
used an off-the-shelf Nation Tule paigey inside our per page 415. We can just change that to get different pagination a regular index. I am just doing pagination here. But otherwise this is exactly like how you would do it with a regular model and then familiar things like customer find by the params ID and customer knew all this stuff Works customer creation is slightly different because I want to be able to get at the API response and that's something that I think we can refine a bit more. I
think some of the active model stuff. So straightforward that the implementation is extremely clear. Where is how to best do it with active record is really more discretion. You have to decide how do we best Xposed? Maybe I can do and make it look very similar to L'Oreal's can do for example, where doesn't make a lot of sense because you don't have a SQL query against an API provide something else to get parameters in queries update also quite similar. But again, we're going to carry the API response. So it's a little bit different than regular rails model. Delete is just
exactly identical to the same thing with like customer prams. So and then likewise in the view we are going to be able to use things that were like link to us and a URL passing in the model as the URL and because of active model model that is actually going to wire up and work correctly. so I go back to the slides. So this Square Ruby is currently playing at our Square connect gem, but as Republic are new Ruby SDK will be leaking into that. And if you don't have the comments that pleased to meet us at Square table on Twitter Enchanted Skipper
and an engineer and evangelist is square and really happy to be working on our public Ruby infrastructure. So I had time to take some questions. Awesome at actually repeat your question cuz I don't think we have a mic here. I will publish it. It's not if you want to I will publish it. I don't have it in a public repo right now, but I will remedy that after the talk. I'll will tweet out a link to the slides from twitter.com underscore h a v e n n. I picked the worst Twitter name ever if the question was on his way to the slides and I will put that up but don't start your Twitter
name with an underscore be unsearchable, which I've never gone years does not get you any followers. But if you manually type in my name, I promise that really is my account. Yes. So actually that was a question was how do you handle both update? And actually the other endpoint that I am not old is our time login point and it does have both updates and that poses more of an editorial dilemma for the for the author because You absolutely can support it. But you're going a little further off the rails and I think that's a really interesting
point for trying to back a model with something is like you really need to make sure that it is thick and did work for both updates, but it's not it's a little bit more alien of an interface. So I think one of those things that is really good to look at for like should I be doing this like I can but should I be is like, can you do you create it read it updated and delete it is it cried. If it is it's a good sign that a model makes sense because you're going to get all these helpers under the hood for both updates and I think some of the book updates real sick stuff is
implementable. I haven't done that, but I think that would be really interesting to look at and I'd love to That okay. So the question was what else can you use active resource for other than Json API and the answer is nothing active resource is very very specifically meant for not eating not just a Json API but a rails Json API. So if you're going to model if you have one rails app and you want to model the active record resource from another rails app, you should not Implement your own after model. You should instead use active resource because it
Buy this talk
Access to all the recordings of the event
Buy this video
With ConferenceCast.tv, you get access to our library of the world's best conference talks.