How to Extend a Class from an Initializer and Have It Reload in Development Environment

How to extend a class from an initializer and have it reload in development environment?

environment.rb

config.to_prepare do
User.send :include, Qwerty::Core::Extensions::User
end

The code is the block is run before every request in development mode and once in production mode.

How to properly extend ApplicationRecord in Rails 6 with Zeitwerk

Figured it out! The issue was there was an explicit require in an initializer that loaded ApplicationRecord.

# config/initializers/setup_other_fancy_thing.rb
require 'application_record'

module OtherFancyThing
def also_fancy
puts 'also fancy'
end
end

ApplicationRecord.send(:include, OtherFancyThing)

The way I debugged this was that I:

  1. Created a new Rails app of the same version and could not reproduce the error
  2. I copied the default application.rb and development.rb and still got the error
  3. Moved the entire config/initializers directory to a temp directory and it made the warning go away! So, I knew it had to be one of the initializers. From there it was just a matter of dividing and conquering until I found the offending initializer.

Reload rails initializers

Initializers are only loaded when starting rails (and never reloaded). When tinkering in config/initializers you will have to restart rails every time.

Of course, you could make sure your code is defined in /lib so you can still make sure it works, by using your test-suite.

E.g. in lib/speaker.rb write

module Speaker
def speak
puts "Ahum, listen: #{self.to_s}"
end
end

and in your initializer you could then do something like

class String
include Speaker
end

While this will still only get loaded when starting rails, you can develop and test your module more easily.

Hope this helps.

Ruby on Rails: Configure API in initializer in development

Well, until someone comes up with a better solution, I've come up with two workarounds. I went with 2.

  1. Don't autoload the lib directory (meaning don't autoload the API). That means having to restart the server when the API code changes, but solves the problem. That's why configuring gems like this works -- because they aren't autoloaded.
  2. Manually reload the initializer in development at the end of lib/my_api.rb:

    load Rails.root.join('config/initializers/smart_meter.rb') if Rails.env.development?

The MyApi constant will be replaced by a new one when Rails autoloads classes. The configuration is still available on the old object:

Loading development environment (Rails 4.2.0)
irb: warn: can't alias context from irb_context.
irb(main):001:0> MyApi.my_setting
=> "foo"
irb(main):002:0> OldMyApi = MyApi
=> MyApi
irb(main):003:0> reload!
Reloading...
=> true
irb(main):004:0> MyApi.my_setting
=> nil
irb(main):005:0> OldMyApi.my_setting
=> "foo"
irb(main):006:0> load Rails.root.join('config/initializers/smart_meter.rb')
=> true
irb(main):007:0> MyApi.my_setting
=> "foo"

Extend model in plugin with has_many using a module

I think this should work

module Qwerty
module Core
module Extensions
module User
# Instance Methods Go Here

# Class Methods
module ClassMethods
def relate
has_many :hits, :uniq => true # no method found

before_validation_on_create :generate_code # no method found
end

def something # works!
"something"
end
end

def self.included(base)
base.extend(ClassMethods).relate
end
end
end
end
end

The old code is wrong cause the validation and the association are called upon module loading, and this module knows nothing about ActiveRecord. That's a general aspect of Ruby, code inside class or module bodies is called directly when loaded. You don't want that. To get around that you can use the above solution.

Why do includes in Rails Engine initializers malfunction when cache_classes = false?

I managed to solve this problem using a to_prepare block instead of the initializer. The to_prepare block executes once in production and before each request in development, so seems to meet our needs.

It wasn't obvious when I was researching Rails::Engine since it is inherited from Rails::Railtie::Configuration.

So instead of the code in the question, I would have:

module MyApp
class Engine < ::Rails::Engine
config.to_prepare do
AnotherApp::Product.send :include, MyApp::ProductExtender
end
end
end

How to configure DCEVM just to reload only modified class?

HotswapAgent is free alternative to JRebel. It is extension to DCEVM that supports many Java frameworks and servlet containers. There is also documentation howto setup IDE to use DCEVM + HotswapAgent correctly.



Related Topics



Leave a reply



Submit