[MUSIC] In this lecture, we will discuss enhancements to ThingImage query scopes. We're going to encapsulate the SQL queries that we just previously discussed into ThingImage query scopes, with an eye on efficiently loading Thing and Image information while querying for ThingImages. And allowing interfacing code to be able to mix and match these query scopes in order to satisfy requirements. So if we come back to the code, we find ourselves in the position where the SQL code that we demonstrated in the last lecture is all still working. And now what we want to do is encapsulate the SQL code that we demonstrated within the spec and roll that into query scopes within the ThingImage. But before we get started focusing on the test, I want to refactor a few things flat out. So within ThingImage, our legacy implementation of with_name and caption does an inner join on a thing and an inner join on an image and goes ahead and does things really specific to that particular data type. Let's create it a little bit more refactored and reusable. And so when we go to implement our join on Thing, let's start out with what we have to find for with_name. Except we don't want an inner join, we would like a left outer join because we're going to want to find images that don't have associated things. And then we want to finish up our query scope of bringing in all of the attributes of Thing images, and making sure that they're applied and applied first in the select clauses. Now with_name can be rewritten, we can get rid of this joins expression. Oop, change that to be with_thing. Make that be with_thing and then with_thing has already taken care bringing in thing_images asterisk. And now it just brings in name. That looks pretty clean, doesn't it? Let me make the font a little smaller to make sure we don't have any unexpected word wrap. Okay, now the same thing is going to happen with caption, we're going to refactor that into a with_image. And then instead of an inner join, we are going to use a right outer join, because we may have images without thing_images, because there's no thing. And then if we are bringing in images, we want to do a select * on all the thing_image attributes. And we don't really want to bring in caption, let's change this raw statement to be thing_images.*, and then we'll bring in images_id, okay. Because if there's not a thing_image, something has to be there to express the image_id. And then of course, you got it, we can rewrite the with_caption to start out with with_image, and then since with_image has already taken care of thing_images, we can get rid of that, and just express caption. And then to do a quick sanity check, we can say ThingImage.with_name.first.attributes, and see that we're getting the B&O Railroad Museum for the thing_name, and we've done this before, right? And then we can add to that, with_caption. And along comes the caption. And then we can add to that cases where we don't have a thing. And then we get a very sparse object that has a image_id, and its caption. Okay, looks like we're on solid ground here. Now we're about ready to look into the meat of the test. Why are we failing here? It's because we don't have a .with_position. .with_position, that would give us lat and long, that would be useful. So let's add a query scope very much like .with_caption saying with_image, and let's bring in longitude and latitude. And that makes our first test happy, which was just saying, did you supply our basic query scopes. All right, the next test is looking to see whether we're going to get pure things, it's going to add the things scope. And although it is going to call this within_range and we will offer this within_range so we can pass the test, we're not going to do a lot of range testing immediately. We'll get to it in a couple of tests down the road. But this kind of shows how it doesn't get in the way if you didn't supply things. Also notice that I don't really need with_position for within_range because we're going to put that within the query scope, so I can delete that. Okay, so if I say within_range give me things. So let's add a query scope of within_range, and let's start out this query scope with embedding in it with_position, and then have it call the geo kit rails within query, if a limit was supplied. And a limit is not being supplied, so we're not really querying anything at the moment. And then we want to make sure that we're returning the query scope. Once we've put that query scope in place, we're actually passing two additional tests. The first is just looking for thing_images coming back that express the primary image for things, they all should have a priority 0. The second is saying that we should be able to express orphans. And so our within_range is not doing a whole lot yet but what it did is it brought in with_position. And with_position brought in not only these position arguments, but it brought in the image which brought in a right join, which made that all work, okay. All right, so now we're at a point where we want to supply distance. And what we would like to do is not so much implement a query scope, but we would like to implement a helper method that accepts a query scope and an origin. And then returns back the results that have an expression for distance that obviously has the correct value. So what we're effectively going to do is implement the distance collection construct, all right, and we're going to do it our way. So, as I said, let's implement it as a helper method of the class, so a class method. Which means we do not need to have an instance of a ThingImage. It'll accept two arguments, an origin and a scope, and then within that scope what it's going to do is it's going to add a select. This was my technique of adding support to the class for distance. And we're going to add to the query with_position because we're going to need that information. So it's going to bring in the longitude and the latitude and then for every element in that query scope, we're going to calculate distance from the origin, and assign it a value for it's distance. Okay, so the test says we're supplying a distance, so it does that check by calling with_distance passing in the origin, and a query scope. And accepting the results of that query scope back, walking the collection, verifying that each element supports a property called distance and verifies it has the right value. We noticed it also passed finding all things without a limit. And, of course, we saw that earlier where we implemented our method such that if the distance property was nil, we would just keep on going. And we got back the two images per thing plus the orphan image. Okay now we're going to call that with_distance with a range, 70, we expect to get back five results and everyone of those results is supposed to be within 70 miles, and that was passing too. All right the parts that aren't passing right now, and it's to be expected, is the order_by, ascending and descending because we didn't finish implementing that within_range method. What we need to do is start paying attention to the reverse property. It's either going to be false if we wanted ascending or true if we want it to be descending. Okay, so we add one more clause in here, and we'll use what I'll call the order_by, because that's what it results in, by distance according to the origin, and passing in reverse. And only apply this if reverse is supplied and we expect it to be either a true or a false. Okay so at this point all our tests are passing. And we seem to have a pretty good set of query scopes defined ThingImages within range ordered by distance, that could either be for Things and could be for orphan images, pretty powerful neat stuff, don't you think? In summary, the encapsulated geolocation queries within ThingImage using query scopes and a helper method. We refactor with_thing and with_image out, so that we could reuse them across position, caption and name. And created an aggregated query, like within range. If you do a couple of them together and then we implemented the distance annotation mechanism so that it worked efficiently with our queries. What's next, PostalAddress and Location Value Object. Let's finish out our database model by implementing a couple more value objects that we'll be needing down the road to fully express the location of an object.