Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rails 6 with Zeitwerk: How to Extend Ruby Core Classes like Date, Time, String etc

I am upgrading a Rails 5.2.2 to Rails 6.0.0 that now has Zeitwerk.

Previously I had been extended core ruby classes like Date, Time, String, Float etc as described in this question. Using an initializers file to load all files from the lib/core_ext/* folder. When starting a rails server it now errors an the last line of the stacktrace reads:

/home/username/.rbenv/versions/2.5.0/lib/ruby/gems/2.5.0/gems/zeitwerk-2.1.10/lib/zeitwerk/loader.rb:351:in `const_get': uninitialized constant CoreExt::Date (NameError)

Unfortunately, Zeitwerk is causing an error where lib/core_ext/date.rb etc is throwing an error that it has already been defined (when using Rails.autoloaders.log! in application.rb. CoreExt::Date

I have since moved the files directly to initializers (previously I just had the initializers directory with a file that loaded each file from the 'lib/core_ext/* folder). This has fixed the issue for now but I'd like to keep core_ext folder and files where they were.

What have I missed here?

lib/core_ext/date.rb

class Date
  def to_sap
    strftime('%d.%m.%Y')
  end
end

I have tried wrapping explicitly in CoreExt but that did not help.

module CoreExt
  class Date
    def to_sap
      strftime('%d.%m.%Y')
    end
  end
end
like image 428
Jay Killeen Avatar asked Sep 23 '19 11:09

Jay Killeen


2 Answers

As is saying on https://github.com/fxn/zeitwerk#file-structure

loader.push_dir(Rails.root.join("app/models"))
loader.push_dir(Rails.root.join("app/controllers"))

And by Zeitwerk author: https://github.com/rails/rails/issues/37835#issuecomment-560563560

The lib folder does not belong to the autoload paths since Rails 3. @pixeltrix's is the recommended modern idiom if you want to autoload. Otherwise, lib belongs to $LOAD_PATH and you can require files in that directory.

We can call push_dir to load the lib subdirectories. My solution:

# config/initializers/zeitwerk.rb

Rails.autoloaders.main.push_dir(Rails.root.join('lib'))

or

# config/application.rb

  ...
  config.autoload_paths += [
    Rails.root.join('lib')
  ]

Then CoreExt::Date can be auto loaded.

like image 27
Dat Le Tien Avatar answered Oct 13 '22 01:10

Dat Le Tien


I have the same structure by extending core functionality and adding files in lib/extensions

I solved this by adding

# config/application.rb
#
class Application < Rails::Application
  ...
  Rails.autoloaders.main.ignore(Rails.root.join('lib/extensions'))
end

And I keep initializing extensions as previously:

Dir[Rails.root.join('lib', 'extensions', '**', '*.rb')].each { |f| require f }

like image 88
dpaluy Avatar answered Oct 13 '22 01:10

dpaluy