Brice Sanchez
Brice Sanchez
Senior web developer in Québec city (Canada). I create web applications that help companies to propel their business model.
· Less than 1 min read

How to properly monkey-patch a Ruby Class in Ruby on Rails?

Sometimes, when we use an existing Ruby Class from a third-part gem, we need to monkey-patch it to fit our project’s needs.

Don’t use class_eval

We often see the usage of class_eval to override a Ruby Class.

In this example, we want to permit new params: :show_in_main_menu, :show_in_footer and :css_class.

# app/decorators/controllers/refinery/admin/pages_controller_decorator.rb
Refinery::Admin::PagesController.class_eval do
  def permitted_page_params
      :show_in_main_menu, :show_in_footer, :css_class,
      :browser_title, :draft, :link_url, :menu_title, :meta_description,
      :parent_id, :skip_to_first_child, :show_in_menu, :title, :view_template,
      :layout_template, :custom_slug, parts_attributes: permitted_parts_attributes_params

By using class_eval, you have to copy/paste the all content of the permitted_page_params method.

Now this overriden method is no more synced from the third-part gem and could probably break if the original method is modified in the gem.

Use Module#prepend

Instead of using class_eval, you should use Module#prepend:

# app/decorators/controllers/refinery/admin/pages_controller_decorator.rb
module RefineryAdminPagesControllerDecorator
  def permitted_page_params
    super << %i[show_in_main_menu show_in_footer css_class]

Refinery::Admin::PagesController.send :prepend, RefineryAdminPagesControllerDecorator

By using Module#prepend, we can call the super method to retrieve the original content of the method: an Array in this case. It allows us to just push the new params in this Array.

It’s a simpler and more robust solution!

NOTE: In this guide we use Ruby on Rails, so we will probably need to tell our application how to load these decorators :

# config/application.rb

config.to_prepare do
  # Load application's model / class decorators
  Dir.glob(File.join(File.dirname(__FILE__), '../app/**/*_decorator*.rb')) do |c|
    Rails.configuration.cache_classes ? require(c) : load(c)
Brice Sanchez
Hey! I'm Brice Senior Web Developer in Québec City (Canada)
who love Ruby, Ruby on Rails and Reactive technologies.
I create web applications that help companies to propel their business model.