Removing Duplication Using with_options
Chapters8
Introduce the with_options method and its purpose to extract common options used across several method calls.
GoRails shows how to use with_options to DRY up option hashes in Rails associations and method calls, including nesting and careful load order considerations.
Summary
GoRails host GoRails walks through the with_options method, showing how it helps factor out duplicated options in a block of related method calls. Nick demonstrates the Rails-oriented example from the codebase, explaining how options like dependent_destroy can be shared across multiple associations and calls. He includes practical notes on how surrounding code can affect behavior, such as the order of has_many through definitions when moving options around. The walkthrough also covers nested with_options, where inherited defaults are merged at each level, and shows how this can both simplify and complicate readability. Throughout, Nick emphasizes testing in the console to ensure the merged options behave as expected, noting potential through-association errors that can surface if definitions aren’t ordered correctly. The video ends with encouragement to experiment and share real-world usage in comments, underscoring the value of keeping code DRY while staying mindful of readability. Overall, the lesson provides a concrete, hands-on look at factoring options for associations and other method calls using with_options.
Key Takeaways
- with_options merges a given set of options into every method call inside its block, reducing duplication.
- nesting with_options is supported and each nesting level merges inherited defaults in addition to its own.
- the example uses dependent_destroy across multiple associations to illustrate how options can be shared and applied consistently.
- console experimentation revealed timing-sensitive issues: the order of has_many through definitions must be correct to avoid through associations errors.
- the technique can be extended with further nested with_options calls, but may impact readability and require careful refactoring.
- you can use with_options with explicit receivers (e.g., I18n defaults) or implicit receivers, both leading to the same merged-options behavior.
- this approach is especially useful for DRYing up setup code for associations and their options when building complex models.
Who Is This For?
Rails developers who want to reduce repetition in ActiveRecord associations and other option-heavy method calls, and who are curious about how to safely nest and apply default options using with_options.
Notable Quotes
"with options pass the option and then a block and that then wraps all of the method calls."
—Nick explains how with_options works conceptually by wrapping multiple calls in a single option merge.
"they are using the completion symbol here"
—An example of a specific option being shared across calls.
"Note each nesting level will merge inherited defaults in addition to their own"
—Important behavior about nesting with_options and default merging.
"with options is pretty cool. If you're a fan of keeping things dry and not repeating yourself"
—Closing endorsement of the technique and its practical value.
Questions This Video Answers
- how does with_options merge options in a Rails block
- can you nest with_options in Rails and how does inheritance work
- what are common pitfalls when using has_many through with options
- how to apply default options using with_options in ActiveRecord associations
- is with_options safe to use with implicit vs explicit receivers in Rails
GoRailswith_optionsRuby on RailsActiveRecordhas_manyhas_many throughdependent_destroyI18n defaultsnesting with_options
Full Transcript
All right. What's up everybody? Welcome back to another lesson here at Go Rails. Uh today we're going to be looking at a method that I found uh while just browsing through the VSY codebase. Uh we're going to be looking at this method called with options here. Okay. Now you can use this method to uh factor out any sort of duplication that might be uh occurring with any of the uh methods that you would call within the block of the uh with options method here. Okay. Now in the physicy codebase here they only have the one method being called inside the block that validates presence of right.
So the width options here they could have just onelined that one uh and it probably would have been fine. uh but they are served for the future if you know they would need to add anything else inside here that they want to have a default uh on option for. In this case, they're using the completion uh symbol here. So, let's go uh go over to the API docs here and look at those for the with options method here. So, you can read about this again. It'll say it's a way to factor out duplication of options that are passed to a series of method calls.
And you can look at some examples. Here's a bit of a better one here where they have all these associations that are um being that are getting set up uh and they all have this dependent destroy option, right? You can factor out this duplication uh by using the with options method, right? So, as you see here in the next example, they say with options pass the option and then a block and that then wraps all of the uh method calls. you know, set things up so that each method called within this block will have its options merged uh with this uh option that we're passing here.
So, you can use this on an uh explicit receiver as in this example here uh where they're using I18N to just kind of set up some default options here and then pass in a block uh to set up the other um options for subject and body here. Or you can not do that. uh you can just use an implicit receiver here or a not explicit receiver uh and it'll execute the whole block uh in merging options context. So here's an example of that. So I want to go play around with something like this uh and just kind of see how this behaves here.
So here we've got some code. Uh you can see there's a number of dependent destroy um association options being set up here. I know you can't see these last two, but if I scoot over you can here. So you can see here dependent destroy dependent destroy here. So we could uh factor out this duplication by wrapping those in a uh with options call and passing dependent destroyer as an option that will then be merged into any other options that are being passed uh along here. So let's go ahead and do that. Let's uh let's first try to group all of these together.
Okay. So now we can go above and say with options and we'll say dependent destroy and then pass it a block and then we can go down here in that block off. Let's go ahead and uh fix our indentation here and then now we can simply go and remove all of those uh dependent destroy calls from the ends here. Okay, now this looks good and nice in theory. However, something to be mindful of and this is not something that has anything to do with with options here uh but just setting up associations in general is that note here that we have this has many accounts through account users association being set up.
So, if we try to uh hop in the console right now and do some things in this uh with the user uh model, we're actually going to hit an error because we're trying to use a through association that hasn't been defined yet. Since we move things down, account users is being defined after we're trying to set up this has many accounts through the account users association. So, as we'll see if we go over here uh and try to hop into the console, if we do user.last last and let's save that to a variable. And if we try to call uh let's say accounts for example, you can see here that we get this error that says cannot have a has many through association which goes through account users before the through association is defined.
Okay, so that's something important to know about. So what we need to do and sorry let me uh make this a bit larger for you here is we have to be sure that we define these uh in the proper order. So we need to take uh the has many accounts option or association and and call that later on. So now if we were to hop back into the console here uh let's user equals user.list and then user.ac accounts. Now we will not get that error. So with options is pretty cool. Um if we look at these last two here we can see that these also share a similar option for the foreign key here.
Right. So, and actually the class name, foreign key, and the inverse of stuff, those are that's all duplicated here. And what's cool is if we go back to the documentation for with options here, you can see that with options can also be nested. And then this note here, let's see what this says. Note each nesting level will merge inherited defaults in addition to their own. Now this example the nesting here is interesting um because it really is just trying to indicate or display that you know this if persisted is actually going to be uh overrode uh by this one here since as you can see here that uh the code is equivalent to this call.
So the inherited default for the if key being uh this if persistent one is actually ignored and the if here is used but this here with options being uh can also be nested. Uh, this makes me wonder if we could do something like another with options call here. Do and then we'll end this off. And now in theory, I feel like we should be able to do something like class name account. Okay, so let's see if we can uh go ahead and remove this now and have this all still work. All right, let's get out of our console and restart it.
And let's say user equals user.last again. Okay. So we should be able to say uh user.owned accounts and that seems to work. Uh let's go ahead and check oursel here. If we were to go ahead and comment uh these two things out that nested uh with options call that is and go back into our console here. Recall these. Okay, now we see that indeed we get an error here uh because it's looking for a constant that is uninitialized userowned account. You can see here um missing model classowned account, right? So does seem to be having an effect there.
So what that really means then is that we should also be able to take since everything else here matches, we should be able to take all of this and then paste it there and then we can remove it from all of those. Now, I will say this is getting a little bit difficult to read here. So, we can put some spacing there. All right, let's save that. Let's hop out of here. Let's run this again. Let's get user user. And user owned accounts. And we see that we're working. Let's try this personal account. That also seems to be working as well.
So, with options, really cool. Uh if you're a fan of keeping things dry and not repeating yourself, right? Um this is a great thing to know about its existence so you can leverage it and share you know common setup for uh methods or in this case associations um so you don't have to duplicate that all everywhere. So with that we'll keep this one short and sweet. Hope you enjoyed it. Uh maybe you can find some usage for this around the code bases that you work on and if so I'd love to see it. Share some examples in the comments below.
And until next time take care.
Get daily recaps from
GoRails
AI-powered summaries delivered to your inbox. Save hours every week while staying fully informed.


