How to Use Controller Concerns in Rails 5 for Devise Custom Attributes
In this Ruby on Rails 5 guide we'll walk through how to refactor the Devise strong parameter list and integrate it into a controller concern.
Guide Tasks
  • Read Tutorial
  • Watch Guide Video
Video locked
This video is viewable to users with a Bottega Bootcamp license

In the last guide and we walked through how to add whitelist parameters for our sign up and for our account update systems with devise. It's all working but this is not considered a best practice. There's a principle in software development that is called the single responsibility rule and it says that a class should have a single responsibility. Which means that if you describe what the class does and you have to use the word "and" then it should probably be split into separate modules.

Right now we can say that our application controller is the abstract controller that manages all the controller functionality for our app and it also allows us to whitelists devise parameters, that is definitely not a good thing. We have broken the single responsibility rule by having our application controller also be the spot where we would whitelist all of these things. It may not seem like a big deal when you have such a small application, however, it becomes a very large problem if you were to have a large application. You wouldn't know where to go and make changes, say that you have a giant application and a year down the road you need to add some more Sign-Up items for users. If you do not know where they put the whitelisted parameters you're going to have to spend some time and expend some effort just to find out where the original developer put in the application controller.

It would make much more sense to isolate it and to keep it in its own special module, that is what controller concerns are for. We've talked about models and we've talked about model concerns like when we integrated our placeholder, let's also do that for our application controller. I think this makes more sense because here we can take and extract all of our devise whitelist parameters and we can put them in their own model.

Let's start by doing that. I'm going to create a new concern I'm going to go to controllers/concerns and click new file. Here I can call this let's see if I save we can call this module something like devise_whitelist.rb. Inside of this we have to create a module, this is not a class it's a module. Usually in rails the pattern you'll see is if it's something that's rails specific such as a application controller or a model or something like that usually it'll be a class. If it is a helper piece of code if it's something that you want to pass around and let other classes share it usually it's going to be a module.

The way that helps me remember is I think of modules as being modular. That's not the right way to type it but that's the way I like to think of it because it helps me remember when something's modular you can move it around, you can organize it however you want, you can have other parts of the application call it easily and it kind of lives in its own isolated world.

If you name a file "devise whitelist" then you have to call it DeviseWhitelist. So in other words you can't call this devise underscore whitelist helper or something and then call it devise whitelist your naming here has to match what you have right here including the first word has to be a capital and every other word has to be a capital as well.

large

Say that I spelt this whitelist and had devise_white_list, If I did that then I'd have to make the L capital. If you're wondering why that is it is because the rails parsing engine tries to map the module name with the file name and what it does they have some parsing methods that will make all of this lowercase. When it finds a new letter that is uppercase in the middle the word it puts a underscore right in front of it and then it tries to map the file name to the module or class name.

The reason I wanted to give that side note, early on when I was learning rails, I would occasionally run into bugs that were very confusing because I didn't see any problems with my code. It turned out that I had simply put the wrong type of punctuation or capitalization in one of the names or I name the file different than I name the module and the rails engine didn't even find it because it couldn't find that right mapping.

The first thing that we have to do inside of our module is I'm going to say extend active support concern. That's going to give us all the kinds of cool methods that are inside of the active support concern module. This is something we did not have to do with our placeholder but it's possible to do with what we need here. Concerns allow us to include before filters and the syntax for that is we're going to say included and then pass this a block. Now we can take out this before filter. Paste it in the module and now inside of this we can create and just drag in our configure permitted parameters method. We made no real changes this is code organization.

large

Now we have to include DeviseWhitelist in the application_controller.rb and everything should work exactly the same way. Look how much better our application controller looks, instead of having all of those things we can just say hey I want to include the devise whitelist and we can leave all the logic for that right here. This is a much better way of implementing this code.

large

If you type rails s so we can verify that everything is still working. So coming back to Chrome I'm going to hit refresh. Click on logout, register we'll create a new user test4@test.com and I'll just put my name, password and sign up. Everything there still works. Let's go to edit and now that captured it.

If I change it here type in the password to update it everything looks like it worked. We can confirm that if we go back to edits, there you go, it captured my name. Everything from a functionality standpoint is exactly the same as before, now we have done a much better job with organizing our code. Depending on what you're taking this course for, whether you're taking this course so you can start building your own project or if you're wanting to become a freelancer or if you're wanting to work in a dev shop, it's very important to not only get your projects working properly but also to organize your code and to follow best practices. A reason why we do this is so that it can make our applications easier to change in the future.

One of the only constants when it comes to application development is change. The one thing you can be sure of, whether it seems like it's going to be a small application or if you know it's going to be a big one, at some point you're going to be asked to extend the functionality beyond what you originally thought. If you can build your code like this where we can make very small organized kinds of files then it's going to make it much easier to change your files and to extend the functionality and to make the application more scalable in the future.

Let's open up the terminal

  • git status
  • git add .
  • git commit -m "Added controller concerns for devise params
  • git push origin authentication.

Let's update our pivotal tracker. We now have completely customized the attributes. One thing I also want you to notice is that I knew when I was going to create the changes. In other words, when I went to the user's migration and we ran that migration, I knew when I added name that I was going to eventually create a concern that did all of this. D you notice how I didn't go straight into creating this concern. There's a few reasons for that, the first is one I wanted to show you how to do it in small steps. The other reason is even if I was doing this in my own app and no one was watching the code like you're watching me right now, I would have still done the exact same process.

Imagine that I had gone and created a concern for all of this and I would've included it and made all the changes for our new registration and our edit registration, I would have done all that and then opened up the browser and it wouldn't have worked. I would have had no clue on where to go to try to fix the bug. Was the bug in one of these view files, was it with how I called the file name, was it with the concern itself, was it with the migration? There were so many different things that happened, if I would have tried to do something like jumping to implement that concern right away then it would have taken a very long time to debug.

The process I like to follow is to make very small changes and I do not go straight to best practices, best practices are fantastic and you want to implement all of them at some point. However, what I try to do is just take very small things and make very small iterations and the first few steps I'll take usually do not conform with best practices. I could care less. I know I'm going to get to that point at some point, instead what I'm concerned with is being able to get it working and then I can start making these changes.

Say that we would have stopped the last video and we would of came here implemented our concern and then test out in the browser and it didn't work. If that happened I would have known automatically the problem was with the concern, it wasn't with calling it from the application controller or from the view files or in the migration. I'd know immediately that I probably have some type of spelling mistake or a little syntax bug inside of the concern. I would be able to isolate it and go and make the fix much quicker than if I had tried to have gone straight to doing this implementation.

One question I get quite a bit from students, you see me follow a pattern like this where I will go create some type of change in the application, I start building a feature and then they're curious because I implemented it and it starts working. Then I come back and perform a refactor. The question is why didn't you just go straight to the final solution if you already knew where you wanted to take it? The process is very important in software development, if you try to go straight into the final solution and straight into the refactor stage then there's a good chance you're going to run into bugs and it's going to take you longer to try to fix them.

One of my biggest mottos is taking a very small manageable chunks, being able to take all the development process in a very iterative form so that I know that something is working at every small stage and then it's going to make it easier to work with the project in the future.

That is why I've followed that process, in case you're watching and you're curious.

In the next guide we are going to get into virtual attributes.

Resources