DEV Community

Adrien.S
Adrien.S

Posted on • Originally published at blog.siami.fr

Avoid constantize in Rails

What do you do when you need to instanciate a class in Rails, but its name is dynamic?

Let's say we want to serialize an object for use in an API payload and we have to handle different versions.

We could have a MySerializer::V1 and MySerializer::V2 classes.

And to instanciate the correct one, we could do something like this:

serializer_class = "MySerializer::V#{version}".constantize
serializer = serializer_class.new(object)
Enter fullscreen mode Exit fullscreen mode

It works, but we should really avoid it.

It could be unsafe

If the string that is constantized ends up containing user-controlled data, we could be in trouble.

Let's say you are allowing the user to create different record types based on a type attribute.

params[:record_type].constantize.create!(...)
Enter fullscreen mode Exit fullscreen mode

Your front-end could only send types like Article or Category, but a malicious user could send something like AdminUser and create themself an admin account.

This is highly unlikely, but it's still a risk.

It has bad developer experience

  • It's hard to read and hard to follow
  • You can't use go to definition, forcing you to open the file manually
  • When searching for a class name, you won't find the lines with constantize, giving you a false sense of security

How to do instead ?

You could have a whitelist of classes you allow to be instanciated, but this doesn't solve all problems.

What I like to do is use a hash to map some value with the class you want to call, for instance :

SERIALIZER_CLASSES = {
  "v1" => Myserializer::V1,
  "v2" => Myserializer::V2
}

def serializer_for(version)
  SERIALIZER_CLASSES[version] || raise "Unknown serializer for version #{version}"
end

serializer_for("v2").new(...)
Enter fullscreen mode Exit fullscreen mode

I like it because it's explicit, easy to read, and allows you to use go to definition or find the line when you search for a class name.

Top comments (0)