I'm testing out a basic Rails app and I seem to be getting some undesirable caching behavior on a library script that's being require()'ed into my controller script.
Suppose FooController.rb contains the following:
require 'utils' # a library script
class FooController
def some_action
#some_member = do_something() # global method defined in utils.rb
end
end
It appears that changes to utils.rb do not take effect until I restart the Rails server. I don't believe this is due to a misconfiguration of Rails' class caching, since a) I am running in a "development" environment, and b) I can make changes directly to the controller code (e.g., to the some_action method above) that are reflected upon the next execution of the script. I've been testing this with some calls to puts that spam messages into the console.
Is there some behavior in either Ruby or Rails that would cause require()-ed scripts to remain cached? If so, is there a way to configure that behavior?
Rails class reloader is relatively naive. I believe it's only intended to reload things like controllers and models, leaving anything you might be require'ing into your project alone. So, if you have some custom code in the lib directory or elsewhere that you have changed it is expected behavior for you to have to restart the Rails server.
If you want to require each time the code is encountered, you really want load.
http://www.ruby-doc.org/core/Kernel.html#method-i-load
Related
My application has a number of controller concerns in app/controllers/api/V2/concerns
When I work on this in development, everything works well. However, as soon as I push to production, I get the following error when the rails server boots up: Uninitialized constant Concerns::MyConcern (NameError)
My concerns look like this:
module Api::V2
module Concerns
module MyConcern
extend ActiveSupport::Concern
def some_method(some_arg)
# some stuff here
end
end
end
end
I have set up config.eager_load = true in both my production and development environments to try and make them as consistent as possible; however, still not seeing this error occur in production.
What's strange is if I comment out the areas where I include the concerns in the controllers, the application manages to boot up; then, when I go to rails console on the production server and see if I can access Api::V2::Concerns::MyConcern I am able to access it without error. This makes me think that there's some sort of autoloading issue going on where the concerns aren't loaded when they are called in the controllers; however, I was under the impression that Rails loaded up everything in the app/ folder automatically and only areas such as the /lib had to be explicitly added.
As an FYI, my controller sits in app/controllers/api/V2/my_controller.rb
Any ideas what could be happening here?
It's highly probable that the V2 component in your path is causing the issue here. On a case insensitive file system this will be resolved as v2 which works. On a case sensitive file system it will be looking for app/controllers/api/v2/concerns/my_concern.rb which doesn't exist, and won't load.
I have an app with a rails engine i use. I've stripped them down pretty much to the bone, but i get a fault when I start the engine up with multi-threading enabled. I faults out with missing constant the model name i'm calling from the controller. I've reduced it down to just calling the Model.all.pluck(:id). And still get the fault. The models are being invoked via an Ajax call to the controller.
The app runs correctly if i refresh the page and continue, never faulting out again, that I can see.
I think it's actually a multi-treading issue, I'm using Rails 4.1, Ruby 2.12 and Puma 2.8.2 and postgress.
This is happening on my development system a macbook. I will don't see it in the heroku logs when I deploy, but ??
Now somewhat curious. I've made the issue go away, by forcing the app to load each model in the engine, one at a time. Things seem to run smoothly after that.
Any suggestions on how to dubug this
This is directly linked to config.eager_load settings of your app. In production it is set to true so you do not have the circular dependency problem, in development it is generally set to false and that's where rails auto loading systems may trigger this kind of error.
I do believe (though I'm not certain) that you can solve this by not declaring nested classes and modules in one single line
Instead of
class MyModule::MyClass
...
end
Use
module MyModule
class MyClass
...
end
end
As far as I understand, in Rails you don't have to require most of the files that you use, and that most of these files are auto-magically required or included in your code as you reference appropriate classes.
If I understand If I do the following in some arbitrary file, or via running a script with rails runner myscript.rb
myscript.rb
User.delete_all
#set up a default user
User.create(name: "default", password: "default")
This file automatically sees a class it doesnt recognize, User, and understands via its naming convention that the class must be defined in /app/models/user.rb then this code is somehow made available via require or something similar.
My question is: How does rails implement this feature? This is something I'd very much like to understand.
The answer depends a lot on the environment. In production, everything is loaded at boot time and all the classes are cached. In development, the classes are found with const_missing and reloaded when they change. Take a look here at the Rails initialization process.
Check method autoload, whose planned deprecation has been halted for now, and also const_missing, to name just two. Another available mechanism would be eg. to rescue NameError exceptions for uninitialized constants...
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)?
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)
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...
How are require and require_dependency different?
How can require_dependency automatically reload classes in development but require can't ?
I digged into Rails' ActiveSupport::Dependencies and dispatcher.rb code. What I saw in require_dependency's code is it basically adds the constants to an autoloaded_constants array. But it gets cleared in clear_application inside dispatcher after each request.
Can someone give a clear explanation or point me to some resources which will help?
require (and its cousin load) are core Ruby methods. require_dependency is a method that helps Rails handle the problem of dependency management. Long story short, it allows Rails to reload classes in development mode so that you don't have to restart the server each time you make a code change. The Rails framework will require_dependency your code so that it can track and reload it when changes are made. The standard Ruby require doesn't do that. As an app (or plugin/engine) developer you should not have to worry about require_dependency as this is purely internal to Rails.
The magic of the Rails class loading process is in the ActiveSupport::Dependencies module. This code extends the default Ruby behavior to allow code inside your Rails app to automatically load modules (including classes which inherit from Module) using Rails' path and file naming conventions. This eliminates the need for the programmer to litter their code with require calls like you would in a plain Ruby application.
To put it another way, this lets you define class Admin::User inside the file app/models/admin/user.rb and have Rails know what you are talking about when you call Admin::User.new from another part of the application like a controller. Without ActiveSupport::Dependencies involved you would have to manually require everything you needed.
If you are coming from a statically typed language like C#, Java, etc then this might be a surprise: Rails code is not loaded until it is needed. For example, a User model class isn't defined and user.rb isn't loaded until AFTER you try to call User.whatever_method_here. Rails prevents Ruby from complaining about that missing constant, loads code for User, and then allows Ruby to continue as normal.
While I can't speak for your specific need, I would be very surprised if you actually needed to use the require_dependency method from within a plugin or engine. If you follow Rails conventions you shouldn't have to tweak the $LOAD_PATH by hand, either. This is not "the Rails way".
In the world of Ruby and also Rails simplicity and clarity is key. If all you want to do is write a plugin or engine and you are already diving deep into internals then you may consider approaching your problem from a different angle. My gut tells me that you may be trying to do something that is needlessly complicated. But then again, I have no clue what you are doing exactly!! :)
require_dependency is useful in an engine when you want to re-open a class which is not defined in your engine (for example in another engine or Rails app) and have it reloaded. In which case something like this works:
# app/controllers/my_engine/documents_controller.rb
require_dependency MyEngine::Engine.root.join('app', 'controllers', 'my_engine', 'documents_controller').to_s
module MyEngine
class DocumentsController
def show
render :text => 'different'
end
end
end