how do I unload & load a class? - ruby-on-rails

I've got a class in /lib that I'm mucking with and testing via the console. I'm making changes to the class (adding a debugger line for instance) and using reload! but the new or removed line is not reflected in the version of the code that the console is running.
Tried these:
config.cache_classes = false
reload!
config.autoload_paths += Dir["#{config.root}/lib/**/"]
config.autoload_paths += Dir["#{config.root}/lib/service_processors"]
config.autoload_paths << 'lib'
I'm running a saved script that creates an instance of the class. I tried just loading the class at the top of my script, but that seemed to cause some unintended consequences.
So, how do I completely unload a class and then reload it? I'm thinking the script will force an unload of the class, then load the class via the file name.

Not sure it that is the problem, but in your application.rb you need to add the following line:
config.autoload_paths += %W( #{config.root}/lib )
And secondly, to make sure that classes/modules are found correctly on reload, the naming has to follow Rails conventions. This means that snake-casing a module or class name should give the filename, and different namespaces (or nesting) should be in different folders.
Some examples to make this more clear :)
class SomeClass --> /lib/some_class.rb
class SomeHTTPStuff --> /lib/some_http_stuff.rb
class API::Stuff --> /lib/api/stuff.rb
HTH.

Invalid after Rails tag added
Well, to load a class:
load "lib/class.rb"
To reload:
load "lib/class.rb"
Those settings are irrelevant.

Related

Rails 4.1.1: uninitialized constant during initial Rails configuration

I'd like to add a microapp for errors to be processed (file I placed inside the lib folder).
module MyApp
class Application < Rails::Application
config.autoload_paths += %W( #{config.root}/lib )
config.exceptions_app = FooApp.new(Rails.public_path)
end
end
But Rails raises an unitialized constant (NameError) during it's initialization. Recently I found a similar post and tried every solution from there, but got no result. What actually I've tried:
Name a class and a filename according to convention (even tried to simplify to a single word - class Foo, filename lib/foo.rb).
Use config.eager_load_paths += %W( #{config.root}/lib ) rather than config.autoload_paths, same effect. :(
Create an initializer file and load a class with require: require "#{Rails.root}/lib/foo"
It doesn't work - seems initializers are performed after the initial Rails configuration.
Move the file into app/misc but it doesn't help.
Put a class inside a module with the same name, rename a class while it's still in the module - no effect.
The only working solution I found - is to require a file right inside the Rails configuration block, but... it's a freaky solution. Probably there still exists an idiomatic one?

Auto-loading lib files in Rails 4

I use the following line in an initializer to autoload code in my /lib directory during development:
config/initializers/custom.rb:
RELOAD_LIBS = Dir[Rails.root + 'lib/**/*.rb'] if Rails.env.development?
(from Rails 3 Quicktip: Auto reload lib folders in development mode)
It works great, but it's too inefficient to use in production- Instead of loading libs on each request, I just want to load them on start up. The same blog has another article describing how to do this:
config/application.rb:
# Custom directories with classes and modules you want to be autoloadable.
config.autoload_paths += %W(#{config.root}/lib)
config.autoload_paths += Dir["#{config.root}/lib/**/"]
However, when I switch to that, even in development, I get NoMethodErrors when trying to use the lib functions.
Example of one of my lib files:
lib/extensions.rb:
Time.class_eval do
def self.milli_stamp
Time.now.strftime('%Y%m%d%H%M%S%L').to_i
end
end
Calling Time.milli_stamp will throw NoMethodError
I realize others have answered similar questions on SO but they all seem to deal with naming conventions and other issues that I didn't to have to worry about before- My lib classes already worked for per-request loading, I just want to change it to per-startup loading. What's the right way to do this?
I think this may solve your problem:
in config/application.rb:
config.autoload_paths << Rails.root.join('lib')
and keep the right naming convention in lib.
in lib/foo.rb:
class Foo
end
in lib/foo/bar.rb:
class Foo::Bar
end
if you really wanna do some monkey patches in file like lib/extensions.rb, you may manually require it:
in config/initializers/require.rb:
require "#{Rails.root}/lib/extensions"
P.S.
Rails 3 Autoload Modules/Classes by Bill Harding.
And to understand what does Rails exactly do about auto-loading?
read Rails autoloading — how it works, and when it doesn't by Simon Coffey.
Though this does not directly answer the question, but I think it is a good alternative to avoid the question altogether.
To avoid all the autoload_paths or eager_load_paths hassle, create a "lib" or a "misc" directory under "app" directory. Place codes as you would normally do in there, and Rails will load files just like how it will load (and reload) model files.
This might help someone like me that finds this answer when searching for solutions to how Rails handles the class loading ... I found that I had to define a module whose name matched my filename appropriately, rather than just defining a class:
In file lib/development_mail_interceptor.rb (Yes, I'm using code from a Railscast :))
module DevelopmentMailInterceptor
class DevelopmentMailInterceptor
def self.delivering_email(message)
message.subject = "intercepted for: #{message.to} #{message.subject}"
message.to = "myemail#mydomain.org"
end
end
end
works, but it doesn't load if I hadn't put the class inside a module.
Use config.to_prepare to load you monkey patches/extensions for every request in development mode.
config.to_prepare do |action_dispatcher|
# More importantly, will run upon every request in development, but only once (during boot-up) in production and test.
Rails.logger.info "\n--- Loading extensions for #{self.class} "
Dir.glob("#{Rails.root}/lib/extensions/**/*.rb").sort.each do |entry|
Rails.logger.info "Loading extension(s): #{entry}"
require_dependency "#{entry}"
end
Rails.logger.info "--- Loaded extensions for #{self.class}\n"
end

Eager loading of rails lib

It looks like this issue will be solved in Rails 4:
http://blog.plataformatec.com.br/2012/08/eager-loading-for-greater-good/
but until then, I'm wondering how to eager-load modules/classes in my /lib.
In IRB it appears that they are loaded on-demand the first time I try to access:
Foo::Bar.constants
=> []
Foo::Bar::Service
=> Foo::Bar::Service
Foo::Bar.constants
=> [:ServiceBase, :Service]
I have several other classes in that module, and my code depends on being able to look them up using Foo::Bar.const_defined? at runtime - how do I ensure all Foo::Bar's classes get loaded at startup?
I'm already using config.autoload_paths += %W(#{config.root}/lib) in application.rb.
Putting this in root/config/initializers/eager.rb should load all .rb files in that folder:
Dir["#{Rails.root}/lib/*.rb"].each {|file| load file}
For me putting this in application.rb solved the problem
config.eager_load_paths += Dir["#{config.root}/lib/**/"]
Use eager_load_paths combined with ActiveSupport::Reloader's to_prepare hook inside development.rb:
config.eager_load_paths += Dir["app/models/stimodel/**/*.rb"]
ActiveSupport::Reloader.to_prepare do
Dir["app/models/stimodel/**/*.rb"].each { |f| require_dependency("#{Dir.pwd}/#{f}") }
end
Adding your paths to eager_load_paths make sure that Rails loads them when it starts up. To make sure that Rails reloads our models if we do any changes or add new files, we also need to hook into the Reloader's to_prepare hook and manually require the dependency there.

Reload rails initializers

In application.rb, I have
config.autoload_paths += %W(#{config.root}/lib
So when I modify a class under lib, my code is reloaded.
However, when I tried adding config/initializers to autoload, I noticed my code doesn't get updated.
If I'm writing an extension for the string class, I have to restart rails every time I modify my code.
Please advise?
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.
Initializer files are loaded only once when the rails server is started. Restart the server when initialzers values are changed.
For further information see the rails initialization guides.
Auto Reloading 'lib' on change
You can auto reload lib files. Follow Link Autoload and Reload lib directory on change
In Configuring Rails Applications: config.reload_classes_only_on_change enables or disables reloading of classes only when tracked files change. By default tracks everything on autoload paths and is set to true. If config.cache_classes is true, this option is ignored.

Adding lib to 'config.autoload_paths' in Rails 3 does not autoload my module

I place a file name g.rb in side Rails.root/lib folder
The file content is like this:
module Google
end
Then I add
config.autoload_paths += %W(#{config.root}/lib #{Rails.root}/app/delayed_jobs)
to my Rails.root/config/application.rb
However, when I try to invoke Google from rails console, an exception is thrown. The exception goes away only if I execute require 'google'.
Why? Shouldn't my file is autoloaded and shouldn't I access the module without any extra require statement?
Hmm, I discovered an interesting thing. In order for Rails to auto load my class, the class name should be compliant to the file name and the folder structure.
For example, if I want to have Google module autoloaded, I must placed it inside google.rb, directly under /lib (incase I specify autoload from /lib).
If I want to auto load Google::Docs, then I either place it inside google.rb or google/docs.rb
I had a similar problem with getting my module to run on Heroku. In addition to the autoload naming convention stated by Stephen C, I found out that the module code must be require'd due to a threadsafe assumption made by the Rails' production environment on Heroku (even though threadsafe was commented out in my production.rb configuration file.) As soon as I require'd the module file before calling include on the module, everything started to work.
require 'mymodule'
include Mymodule
Please take a look at this excellent article on the subject of getting Modules to load correctly in Heroku (production).
That's because the point of autoload is not to 'require' everything up front (startup penalty). Classes are loaded as they are needed/referenced. In order to do this, you need some way to know where to look for the class. Otherwise, you would have to load every file in the autoload directory in advance to see what classes are declared. It's a tradeoff, but requiring everything in advance (as marbaq suggests) is not autoloading.
You can use the autoload command as provided by Ruby, which takes two arguments, the module to load (symbolized, i.e. :Google in your case), and the second argument is the filename, which would be g.rb if lib is in your load path ($:). See the Ruby docs for autoload.
Change config.autoload_paths to config.eager_load_paths
(based on Rails issue #6850 and Force reload! from lib directory in rails 3.2 console)
I faced the same problem just now, and my "solution" (or rather workaround) was to manually require every needed file from Rails.root/lib in my application.rb.
require 'lib/message'
require 'lib/store'
require 'lib/vault/vault.rb'
require 'lib/custom_loggers'
module MyApplication
class Application < Rails::Application
My next step would be to categorize the files in module folders as you mention.
i found this solution recently
config/application.rb
module AppName
class Application < Rails::Application
# Custom directories with classes and modules you want to be autoloadable.
config.autoload_paths += Dir[Rails.root.join('app', 'models', '{**}')]
config.autoload_paths += Dir[Rails.root.join('app', 'lib', 'extensions')]
end
end
the first config call induces rails to auto-load all sub-directories of the app/models directory
so now i can have /app/models/sub_directory/model.rb auto-loaded
(handy for organising an app with a large code base)
the second config call induces rails to autoload the lib/extensions directory
hope this helps
note: i believe this is rails 3 specific

Resources