How are require, require_dependency and constants reloading related in Rails? - ruby-on-rails

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

Related

Extend Rails within Engine

I did a Rails extension as suggested by
extending rails question using Harish Shetty answer.
I like it so much I decided to use it in my Rails Engine. Didn't quite work.
To make it work i place the file active_record_extensions in the Lib/[ENGINE] directory and added the Require active_record_extension to my engine.rb file.
This works, but I wonder if there's is a more proper way to do it.
Also. I ran into an Issue, (don't know if i'm allowed to piggyback a second question).
I have a Model called Translation. When i use the extended class method with that model i invoke an error 'Circular dependency detected' with
raise "Circular dependency detected while autoloading constant #{qualified_name}"
I'm guessing that using the model extension exposed the name to something that doesn't like the fact that I used Translation. (doesn't happen with the other models I have). I get around it by not using the extend class method with that model.

Understanding the rails relationship between "require" and class naming conventions

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...

Ruby and/or Rails caching require()'ed scripts?

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

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)?
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...

When to consider creating your own Ruby module in a Rails app?

With a Ruby module, you can cluster together a bunch of methods that you might use in one place and then include them into a class so it's as if you had written them in that class.
What kinds of practical uses are there for Ruby modules in a rails app?
I would appreciate if someone could mention an example of where they've actually used a module of their own so I have a sense of what situations I should be thinking about creating them. Thanks.
1) Any time I'm about to duplicate (or substantially duplicate) a piece of code: "oh, i could just cut/paste into this other controller . . . "
2) Any time I write code that is very obviously going to be reused in the future.
3) Code of substantial size that has a specific purpose, where that purpose is fairly distinct from the main purpose of the controller/model. This is somewhat related to (2), but sometimes code won't get reused but a module helps for organization.
You can place them in the /lib directory and they'll be loaded with your Rails project.
For example, you can view this repo of mine of an old project: lib directory of a Rails project
So for example, I have the following module:
google_charts.rb
Module GCharts
class GoogleCharts
def some_method
end
end
end
And anywhere in my Rails app, I can access the methods.
So if I were to access it from a controller, I would simply do:
require 'google_charts'
GCharts::GoogleCharts.some_method
We use modules for functionality that isn't tied to ActiveRecord models and hasn't been abstracted into a plugin or gem.
A recent example from our production code base is a library for integrating with Campaign Monitor for email list management. The core of the system uses our user model, but the actual interaction with the extenrl service is abstracted through a module that lives in /lib.

Resources