Mastering Rails Params: with_defaults

Handling parameters in Ruby on Rails is a common task, especially when working with forms and strong parameters. A lesser-known but powerful method that can simplify this process is with_defaults. This method allows you to provide default values for any missing keys in a hash, making your code cleaner and more efficient. Let’s explore how with_defaults works and how you can use it effectively with strong parameters.

What is with_defaults?

with_defaults is an alias for the reverse_merge method in Rails. It allows you to merge two hashes, where the values from the given hash are only applied if they don’t already exist in the receiver hash. It’s particularly useful when you want to ensure that certain keys have default values, without overwriting any existing ones.

A Practical Example

Let’s look at a practical example involving strong parameters in a Rails controller. Imagine you’re working on a blog application, and you want to ensure that every new post is associated with the current user, but you also want to allow some flexibility for special cases.

Typically, you might define your post_params method like this:

1
2
3
def post_params
  params.require(:post).permit(:title)
end

But what if you want to ensure that every new post is automatically associated with the current_user? You could add a default value for the user attribute using with_defaults:

1
2
3
def post_params
  params.require(:post).permit(:title).with_defaults(user: current_user)
end

With this code, if the post parameters don’t include a user attribute, the current_user will be used as the default. This is a clean and concise way to guarantee that each post is linked to the user who created it, without having to manually set this value in multiple places.

Why Use with_defaults?

A More Complex Example

Consider a scenario where your application needs to allow certain admin users to specify a different author for a post, while regular users should only be able to create posts for themselves. You could adjust the parameters conditionally like this:

1
2
3
4
5
6
7
8
9
10
def post_params
  permitted_params = params.require(:post).permit(:title)
  
  # Set default user to current_user if not provided and user is not admin
  unless current_user.admin?
    permitted_params = permitted_params.with_defaults(user: current_user)
  end

  permitted_params
end

In this example, regular users can only create posts for themselves, but admins can set the user parameter to someone else. This keeps your controller logic clean and your parameter handling robust.