Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does code need to be reloaded in Rails 3?

I am a former PHP developer learning Rails and Sinatra. In PHP, every page request loaded all of the required files. If I changed some code and refreshed the page, I could be sure that the code was fresh.

In Rails 3, Controller code is fresh on every request. However, if I modify any code in the /lib folder, I need to restart the server so the changes take effect.

Why does this happen? Is it something to do with the way Ruby is designed? Is Rails doing some optimizations to avoid reloading code on every request?

Thanks!

Edit: I am mostly interested in what is going on under the hood. Do frameworks like Rails and Sinatra do some special caching for classes? If so, what does they do? Is the default behavior in Ruby that all code gets reloaded on every request? Why do we need tools like Shotgun for Sinatra (http://sinatra-book.gittr.com/#automatic_code_reloading)?

like image 381
Venkat D. Avatar asked Mar 15 '11 08:03

Venkat D.


2 Answers

While you are in development mode you should tell Rails not to cache your classes so they reload each time. This means that each request the classes are basically redefined in the rails interpreter. The setting in your Rails.root/config/environments/development.rb:

config.cache_classes = false

The classes the are in your lib/ dir are usually loaded through an initializer and not subject to this setting.

When you move to production you will want all of your classes to be cached so requests are faster and rails will do optimizations to things like scopes on your models.

You could put something in another initializer (maybe called Rails.root/config/initializers/development_reload.rb) that reloads the lib dir with every request in development (or just the ones you are working on):

# file development_reload.rb
if Rails.env.development?
  ActionDispatch::Callbacks.after do
    load 'filename_in_lib'
    # or
    Dir.entries("#{Rails.root}/lib").each do |entry|
      load entry if entry =~ /.rb$/
    end
  end
end

I am calling "load" so it actually reloads the file, whereas "require" would just check if it has been loaded and determine it already has so it will not reload it. (I just threw this together and don't use it, but Ruby is extremely flexible and will allow you to do quite a bit.) Use something like this wisely and only in a dev environment.

Why code needs to be reloaded in Rails 3?

Ruby is an interpreted language (JRuby has some support for precompilation, but it's still interpreted). Interpreting the the definition of classes once on initialization is similar to compiling php and deploying in executable format (somewhat). The interpreter is not bothered with redefining classes all the time.

Forcing the explicit reload is an optimization for this type of interpreted language. (if you AOT compile in php you would need to reload the compiled "bytecode" after changes as well; default php uses on-the-fly compilation which is what you are taking advantage of)

like image 118
Brandon Avatar answered Oct 04 '22 20:10

Brandon


How about a more high level approach:

    ActionDispatch::Reloader.cleanup!
    ActionDispatch::Reloader.prepare!

This was taken from Rails/ActiveRecord v3.2.13 - active_record/railtie.rb

The load approach didn't work for me. Just performing load caused a weird issue where it would trigger certain validators twice for me.

In order to fix that, I tried Object.send(:remove_const, User) before reloading User, but then I lost my observers on that class, so I started chasing my tail.

The above approach reloads all the classes, so maybe there is still yet a better approach to properly remove an individual class from cache and reload it...

like image 21
Peter P. Avatar answered Oct 04 '22 20:10

Peter P.