83

If on Ruby on Rails, I need to add a method called

class String
  def capitalize_first
    # ...
  end
end

and wonder where should the file go to? (which directory and filename, and is any initialize code needed?) This is for a Rails 3.0.6 project.

nonopolarity
  • 146,324
  • 131
  • 460
  • 740

3 Answers3

147

I always add a core_ext directory in my lib dir.

Create an initializer for loading the custom extensions (for example: config/initializers/core_exts.rb). And add the following line in it:

Dir[File.join(Rails.root, "lib", "core_ext", "*.rb")].each {|l| require l }

and have your extension like:

lib/core_ext/string.rb

class String
  def capitalize_first
    # ...
  end
end
Ken Ratanachai S.
  • 3,307
  • 34
  • 43
Mike Lewis
  • 63,433
  • 20
  • 141
  • 111
  • I wonder... should we extend the `String` class or just use a helper method in view? But then, if it is a helper, then it is also troublesome to make it work in controller (or model), and it cannot be chained like `s.capitalize_first.truncate(30)` – nonopolarity Apr 13 '11 at 20:43
  • 15
    forgive my ignorance, but could you give an example of an "initializer config" thanks – Brian Sep 08 '11 at 18:02
  • 15
    I placed it in config/application.rb: `class Application < Rails::Application ... config.autoload_paths += Dir[File.join(Rails.root, "lib", "core_ext", "*.rb")].each {|l| require l } ...` – d3vkit Nov 26 '12 at 23:33
  • Definitely the way to go if you want a scalable solution – watzon Jun 15 '15 at 23:12
  • 5
    If you put the extension within `lib/core_ext/string.rb`, don't forget to include `require 'core_ext/string'` at the top of the file where you use it. – Rok Strniša Jan 11 '16 at 23:37
  • I needed to add `app` to the file path: `Dir[File.join(Rails.root, "app", "lib", "core_ext", "*.rb")].each {|l| require l }` – moondaisy Nov 08 '17 at 12:25
  • 1
    @moondaisy you are wrong. `lib` directory should not be inside the `app` directory. – Fabrizio Bertoglio Dec 15 '17 at 10:36
  • Rails 5 seems to have changed how it loads class files (e.g. `config.autoload_paths`), which could cause some issues. – Backo Jan 11 '19 at 19:02
  • How do you do it in Rails 6? – Taschetto May 26 '20 at 17:49
  • @Taschetto Add the line `Rails.autoloaders.main.ignore(Rails.root.join('lib/core_ext'))` at the start of the initializer to prevent conflicts with the "new" Zeitwork autoloader. Since you manually load the files in the initializer anyway, they can be excluded from being autoloaded by Zeitwork. For more info, see: https://github.com/fxn/zeitwerk#ignoring-parts-of-the-project – 3limin4t0r May 13 '22 at 16:32
  • @FabrizioBertoglio See https://stackoverflow.com/questions/52284058/why-use-app-lib-instead-of-lib-in-rails. – Alexey Romanov Mar 13 '23 at 11:11
66

You could do it in config/initializers/string.rb

class String
  def capitalize_first
    # ...
  end
end

should be all you need (besides an app restart).

njorden
  • 2,606
  • 20
  • 23
  • 1
    I'm curious to where would you add a test file for this extension? – konyak Jul 29 '15 at 16:59
  • 3
    Easiest method, yes. But you loose support for reloading this class when you make changes in development. – Josh Jun 30 '16 at 20:20
6

The guidelines in Rails 3.1 is the way to go:

http://guides.rubyonrails.org/plugins.html#extending-core-classes

If you follow the default convention you won't need to mess with an initializer config.

Hopstream
  • 6,391
  • 11
  • 50
  • 81
  • 1
    This method does not eager load the extension. It loads it after you call the module/class name (from this rails guide, it's Yaffle). This means you won't be able to call the extension method until the module name (Yaffle) is called first and you cannot define this module name in initializers. I've tested it in the console and it's how autoload_paths works. Requiring the extension file in config/initializers/ or in config/application.rb are the best options. – konyak Jul 29 '15 at 19:43