3

Reedition of the question (after and thanks to Gavin Miller)

If a have three kind of core extensions (or monkey patches), like these examples:

1- a Form builer

class FormWithBuilder < ActionView::Helpers::FormBuilder
  def object
    # ... my code
  end
end

2- class core extension

class Hash
  def translate_values
    th=Hash.new
    self.each{|k,v| th[k]=I18n.translate(v)}
    th
  end
end

3- A Form helper

module ActionView
  module Helpers
    module TranslationHelper

      alias_method :original_localize, :localize
      def localize(*args)
....
      end
     end
  end
end

where do I have to put these files?

After Gavin Miller response, I understand I have to use the correct folder structure , as Rails core extension and so that. And not havig them under config/initializers that was where I had before (and working). OK, lets create folders and put them under lib/...

and looking forward deeply I've realized that using config.eager_load_paths and config.autoload_paths is a bad practice:

https://edgeguides.rubyonrails.org/autoloading_and_reloading_constants.html#autoload-paths-and-eager-load-paths

https://github.com/rails/rails/issues/13142

and recomends to put all this lib/core_extensions folder that Gavin Miller said to me, under app/lib/ folder and nothing else, but this solution does not work for me, unless I require all files, but this is what, as I understand, is not necessary because everything inside app is autoloaded.

I think I'm missing some configuration

This is in images, easier to explain

enter image description here

rails c

Running via Spring preloader in process 9724
Loading development environment (Rails 5.1.5)
2.4.0 :001 > Hash.new.translate_values
NoMethodError: undefined method `translate_values' for {}:Hash

after require it:

2.4.0 :015 > require Rails.root.join('app','lib','core_extensions','hash','localization.rb')
 => true 
2.4.0 :016 > Hash.new.translate_values
 => {} 

Thanks

---- Edited on 27/10

IS this a bad solution? Just create a file under config/initilizers/core_extensions.rb and

Dir.glob(Rails.root.join('lib/core_extensions/**/*.rb')).sort.each do |filename|
  require filename
end

this way I don't need to modify config.autoload_paths neither eager_load_paths

Albert Català
  • 2,026
  • 2
  • 29
  • 35

1 Answers1

2

What you're calling redefinitions are called core extensions (or monkey patches), and get dropped in a folder called core_extensions. That way instead of a large group of varying functionality in a single file, you can break each piece of functionality into individual files. The Rails project illustrates what I mean folder structure wise.

Initializers is one place to put them, but I tend to prefer following the Rails convention of lib/core_extension/class_name/<file_name>.rb since they're easier to get tests around, follow convention, and can more easily be extracted into a gem if the need arose.

Looking at your specific files, I'd go with:

- lib
  - core_extensions
    - hash
      - localization.rb
    - action_view
      - helpers
        - translation_helper
          - localization.rb

The FormWithBuilder is not a core extension, but a subclass. lib is an appropriate place to put that, but don't conflate the idea of subclasses and core extensions, they're two very different constructs that serve very different purposes.

Gavin Miller
  • 43,168
  • 21
  • 122
  • 188
  • Thanks, just a question: lib is not automatically loaded in a rails project, so that, for example, I will have to `'require core_extensions/action_view/helpers/transltaion_helper/localization'` in all files (*.html.erb) I want to use `localize`, isn't it? and that can became a litte cumbersome – Albert Català Nov 22 '18 at 18:33
  • @AlbertCatalà for that case, you can add lib to your autoload_path: https://stackoverflow.com/a/19650564/33226 it's usually one of the first things I do for a rails project if it hasn't been done already. Not sure why rails doesn't do that be default. – Gavin Miller Nov 22 '18 at 18:41
  • Thank you! for all. – Albert Català Nov 22 '18 at 18:49
  • @AlbertCatalà You can eager load the *lib* folder by adding `config.eager_load_paths << Rails.root.join('lib')` to your *config/application.rb*. When eager loaded you can drop the *require* statements. – 3limin4t0r Nov 26 '18 at 09:51
  • Don't add the *lib* directory into the *app* directory, they both have a distinct purpose. An explanation of the different directories can be found in the [Getting Started with Rails](https://guides.rubyonrails.org/getting_started.html#creating-the-blog-application) guide. – 3limin4t0r Nov 26 '18 at 09:59
  • Well, I'm not expert in this configuration, but here (https://edgeguides.rubyonrails.org/autoloading_and_reloading_constants.html#autoload-paths-and-eager-load-paths) recomends not to use eager_load_paths neither autoload_paths – Albert Català Nov 26 '18 at 14:36