How is the config symbol resolved in environments/*.rb - ruby-on-rails

This is how a typical config/environments/*.rb file begins:
MyApp::Application.configure do
config.cache_classes = false
...
end
The block passed to configure dereferences the symbol config which is apparently unbound. How does this technically work? The symbols used in a block/Proc/lambda should be bound in the context of its declaration, not left to be resolved in the dynamic scope at the call site.
A related question is, where exactly is the Application.configure method declared? It's not in either application.rb, engine.rb, or railtie.rb. Maybe if I managed to find this method, I would have found the answer to my main question.
Also related, I have studied the Rails initialization procedure in excruciating detail, and I can't find even a mention of a config/environments/*.rb file. If I knew how these files were treated by the init procedure, that may shed some light on this.

It's a method config in Rails::Application in the railties gem in lib/rails/application.rb which returns an instance of Application::Configuration, defined in lib/rails/application/configuration.rb.
The method configure is contributed to Railtie from the autoloaded module Configurable, lib/rails/railtie/configurable, and is defined as
def configure(&block)
class_eval(&block)
end
which explains why the block that configure accepts can resolve the config symbol. Note that class_eval is another piece of rubyist magic that makes this work: it rebinds the passed-in block's self symbol to the call site's class.
Check the comments in the first file in the Booting Process section, which explains where, how and in what order all this goodness comes from, including how the /config/environments directory is processed.

Related

Accessing models and modules in a Rails 7 initializer

This issue relates to a need to set a Rails config variable as the application boots, and the value of that variable needs to come from data in the database (which are then modified). So, I have an initializer with something like this:
require "#{Rails.root}/lib/modules/facet_altering.rb"
include FacetAltering
Rails.application.config.reject_subjects = FacetAltering.reject
The reject method is potentially slow and calls the Subject model (which includes some concerns).
If I try to require subject.rb, application_rb and the relevant concerns from app/models then I progress a bit further, but eventually get stuck on uninitialized constant Subject::MySpecialConcern.
There might be some better way to set the reject_subjects value; I'd prefer not to run FacetAltering.reject each time the value of reject_subjects is used, though this might be an easy 'fix' if no other solution arises (at the cost of slowing things down). Or, is there another way to access these classes as the application boots?
Edit: Following on from the comment below, this is in config/application.rb:
%W[#{Rails.root}/lib/modules #{Rails.root}/test/mailers/previews].each do |path|
config.eager_load_paths << path
end
This post offered a useful clue:
Rails Model no longer available in initializer after upgrade to Rails 7.0
So, putting my code in config/application.rb as follows did the trick:
config.after_initialize do
Rails.application.config.reject_subjects = FacetAltering.reject
end
Now to find the answer to RuntimeError: Foreign key violations found in your fixture data. Ensure you aren't referring to labels that don't exist on associations. and I might be able to complete this Rails 6 -> 7 upgrade!

Rails unable to autoload constant from file despite being defined in that file

This is a tricky one to explain. I have a module in another module namespace like so:
# app/models/points/calculator.rb
module Points
module Calculator
def self.included(base)
base.send(:include, CommonMethods)
base.send(:include, "Points::Calculator::#{base}Methods".constantize)
end
end
end
So then in other classes all I need to do is:
class User
include Points::Calculator
end
I've specified this directory in application.rb to be autoloadable...(even though i think rails recurses through models...)
config.autoload_paths += Dir[ Rails.root.join('app', 'models', "points") ]
In development env, everything works fine. When running tests(and production env), I get the following error:
Unable to autoload constant Points::Calculator, expected /Users/pete/work/recognize/app/models/points/calculator.rb to define it (LoadError)
I actually followed the advice here to fix the problem: Stop Rails from unloading a module in development mode by explicitly requiring calculator.rb in application.rb.
However, why is this happening??
I stuck some debug output in ActiveSupport's dependencies.rb file and noticed that this file is being required twice. The first time its required I can see that the constant is indeed loaded.
But the 2nd time its required the constant has been unloaded as far as Rails can tell, but when the actual require is called, ruby returns false because ruby knows its already required it. Then Rails throws the "unable to autoload constant" error because the constant still isn't present and ruby didn't "re-require" the file.
Can anyone shed light on why this might be happening?
Rails augments the constant lookup mechanism of ruby.
Constant lookup in Ruby:
Similar to method missing, a Module#constant-missing is invoked when a reference to a constant fails to be resolved. When we refer to a constant in a given lexical scope, that constant is searched for in:
Each entry in Module.nesting
Each entry in Module.nesting.first.ancestors
Each entry in Object.ancestors if Module.nesting.first is nil or a module.
When we refer to a constant, Ruby first attempts to find it according to this built-in lookup rules.
When ruby fails to find... rails kicks in, and using its own lookup convention and its knowledge about which constants have already been loaded (by ruby), Rails overrides Module#const_missing to load missing constants without the need for explicit require calls by the programmer.
Its own lookup convention?
Contrasting Ruby’s autoload (which requires the location of each autoloaded constant to be specified in advance) rails following a convention that maps constants to file names.
Points::Calculator # =>points/calculator.rb
Now for the constant Points::Calculator, rails searches this file path (ie 'points/calculator.rb') within the autoload paths, defined by the autoload_paths configuration.
In this case, rails searched for file path points/calculator in its autoloaded paths, but fails to find file and hence this error/warning is shown.
This answer is an abstract from this Urbanautomation blog.
Edit:
I wrote a blog about Zeitwerk, the new code reloader in Rails. Check it out at -> https://blog.bigbinary.com/2019/10/08/rails-6-introduces-new-code-loader-called-zeitwerk.html
If someone is having this issue in rails 6 which has zeitwerk autoloader,
Change ruby constant lookup back to classic in your application.rb
# config/application.rb
#...
config.autoloader = :classic
#...
Read more details here Rails Official Guides
Calculator should be a class to be autoloaded correctly
module Points
class Calculator
...
end
end

How to automatically include Gem in the path?

I have foo.gem and there is lib/foo.rb in there.
When I add gem to Gemfile it's foo.rb is automatically required in my path. But I need to include it automatically. Reason for this is I am making console extension and I want them to be available without me writing `include Foo'.
I am experimenting with
SOME_CLASS.send(:include, Foo)
But not sure what class to use to have it added to the path e.g. when I start console that is automatically included. Here are some mixins automatically included in console, I need mine to be there :) Thank you
irb(main):006:0> self.class.included_modules
=> [PP::ObjectMixin, JSON::Ext::Generator::GeneratorMethods::Object, ActiveSupport::Dependencies::Loadable, Kernel]
P.S.
I can solve problem with initializer but I don't want to change project's code I just want to add gem and that it works.
You should use the Kernel module, which is included by Object. It's where private methods like exit, puts and require are defined, so it is an excellent choice for defining an imperative API.
When you extend Object, people expect to be able to call your methods explicitly on any object, and they also understand that your method depends on that object's state.
Kernel methods understood differently. Even though they're technically available to all objects, you don't expect people to write things like:
'some string'.sleep 1000
This makes no sense. sleep has nothing to do with the string; it doesn't depend on it in any way. It should only be called with an implicit receiver, as if the very concept of self didn't exist.
Making your methods private and extending Kernel instead helps you get that message across.
You can do that in foo.rb:
module Foo
# …
end
Some::Class.send :include, Foo
When you load or require some file, it is executed line by line. You can put arbitrary code anywhere in the file, even inside module and class definitions. You can take advantage of that in order to properly set up your library so that others don't have to.
Did you tried
Object.send(:include,Foo)
or
self.send(:include,Foo)
inside your console

Explain to me how config works in Rails

I have a Rails 3 application, call it "MyApp". In my config\environments\production.rb file I see such things as
MyApp::Application.configure do
config.log_level = :info
config.logger = Logger.new(config.paths.log.first, 'daily')
...or...
config.logger = Logger.new(Rails.root.join("log",Rails.env + ".log"),3,20*1024*1024)
So, questions are focusing on terminology and wtf they mean... (or point me to some site ,I have looked but not found, to explain how this works.)
MyApp is a module?
MyApp::Application is a ...? What, a module too?
MyApp::Application.configure is a method?
config is a variable? How do I see it in console?
config.logger is a ???
config.paths.log.first is a ...??
--in console I can see "MyApp::Application.configure.config.paths.log.first" but don't know what that means or how to extract info from it!?!
Is this too much for one question? :)
I have looked at the tutorial http://guides.rubyonrails.org/configuring.html but it jumps right into what things do.
A six sided question! Oh my. Let's ahem roll.1 Here's hoping I receive 6 times the upvotes for it then? :)
1. MyApp is a module?
Yes, it's a module. It acts as a "container" for all things pertaining to your application. For instance you could define a class like this:
module MyApp
class MyFunClass
def my_fun_method
end
end
end
Then if someone else has a MyFunClass, it won't interfere with your MyFunClass. It's just a nice way of separating out the code.
2. MyApp::Application is a ...? What, a module too?
MyApp::Application is actually a class, which inherits from Rails::Application. This does a quite a lot of things, including setting up the Rails.application object which is actually an instance of MyApp::Application that you can do all sorts of fun things on like making requests to your application (in a rails console or rails c session). This code for instance would make a dummy request to the root path of your application, returning a 3-sized Array which is just a plain Rack response:
Rails.application.call(Rack::MockRequest.env_for("/"))
You can also get the routes for your application by calling this:
Rails.application.routes
The main purpose of defining MyApp::Application is not these fun things that you'll probably never use, but rather so that you can define application-specific configuration inside config/application.rb. Things like what parameters are filtered, the time zone of the application or what directories should be autoloaded. These are all covered in the Configuration Guide for Rails.
3. MyApp::Application.configure is a method?
Indeed it is a method, and it allows you to add further configuration options to your application's configuration after config/application.rb has been loaded. You've probably seen this used in config/environments/development.rb or one of the other two files in that directory, but basically they all use the same options as shown in that Configuration Guide linked to earlier.
4. config is a variable? How do I see it in console?
The config "variable" is actually a method defined within the code used for Rails::Application and returns quite simply a configuration object which stores the configuration for the application.
To access it in the console, just use Rails.application.config. This will return quite a large Rails::Application::Configuration object for your viewing pleasure.
5. config.logger is a ???
The method you're referring to, I assume, comes from this line in config/environments/production.rb:
# Use a different logger for distributed setups
# config.logger = SyslogLogger.new
The method in this example is not config.logger, but rather config.logger=, which is referred to as a "setter" method in Ruby-land. The one without the equal sign is referred to as a "getter". This method sets up an alternative logger for the production environment in Rails, which then can be accessed by using Rails.logger within the console or the application itself.
This is useful if you want to output something to the logs, as you can simply call this code:
Rails.logger.info("DEBUG INFO GOES HERE")
6. config.paths.log.first is a ...?? --in console I can see "MyApp::Application.configure.config.paths.log.first" but don't know what that means or how to extract info from it!?!
Within a Rails application, you can modify the locations of certain directories. And so, this config.paths method is a way of keeping track of where these directories map to. In my entire Rails life I have never had to use or modify this variable and that can mean either one of two things:
It's not used often by Rails programmers, or;
I don't live a very varied life.
Interpret it as you will. My main point is that you're probably never going to use it either.
I hope these help you understand Rails a little more!
1 Terrible dice joke.
MyApp is a module, it's a namespace including an app you'll launch, see next line
MyApp::Application is a Class and you're running it's instances when running a Rails app
MyApp::Application.configure is a method. It passes all instructions to the class. See Ref.
config is a method or an instance variable (when set) which belongs through inheritance to Rails::Application::Configuration. See Ref.
You can see it in console doing: MyApp::Application.config
config.logger doesn't exist until you define it, so it's a Logger instance. See Ref.
config.paths.log is a Rails::Paths::Path
you can access it in console using: MyApp::Application.config.paths.log

Ruby on Rails: Learning ActionController class - Question on $:.unshift activesupport_path and autoload method

Inside ActionController class (rails/actionpack/lib/action_controller.lib) I found several weird code. I don't really have a mentor to learn Ruby on Rails from, so this forum is my only hope:
Question #1: Could anyone help me explain these lines of codes?
begin
require 'active_support'
rescue LoadError
activesupport_path = "#{File.dirname(__FILE__)}/../../activesupport/lib"
if File.directory?(activesupport_path)
$:.unshift activesupport_path
require 'active_support'
end
end
Especially the line with $:.unshift activesupport_path
In my thought, it tries to require active_support class, and if that doesn't work, it looks if activesupport_path is a directory, if it is, then . . . I totally lost it.
Question #2: What autoload method is for?
module ActionController
# TODO: Review explicit to see if they will automatically be handled by
# the initilizer if they are really needed.
def self.load_all!
[Base, CGIHandler, CgiRequest, Request, Response, Http::Headers, UrlRewriter, UrlWriter]
end
autoload :Base, 'action_controller/base'
autoload :Benchmarking, 'action_controller/benchmarking'
autoload :Caching, 'action_controller/caching'
autoload :Cookies, 'action_controller/cookies'
.
.
.
Question #3: If I later find a method I don't understand what for, how is the best way to find out? As for that autoload method case, I tried to find it across my project (I have my Rails code frozen there) but couldn't find any clue. I searched for "def autoload". Am I doing things wrong? Is my IDE, TextMate just doesn't cut it?
Thank you!
In order for a file to be required you have to ensure that the path to it is in the Ruby $LOAD_PATH variable. This is has a short-hand version $: for legacy reasons, inheriting this from Perl.
When you call require, the interpreter looks for a .rb file in each of the paths given there until it finds a match. If it finds one, it is loaded. If not you get an exception.
Often you will see lines like this in files:
# script/something
# This appends "script/../lib" to the $LOAD_PATH, but this expands to
# something like "/home/user/project/lib" depending on the details of
# your installation.
$: << File.expand_path(File.join('..', 'lib'), File.dirname(__FILE__))
You can use standard Array modifiers on $LOAD_PATH like unshift, push, and <<.
The first block of code is attempting to load active_support and only if that fails does it go about modifying the $LOAD_PATH to include the likely location of this file based on the path to the file making the require call. They do this because typically all gems from the Rails bundle are installed in the same base directory.
The reason for using unshift is to put that path at the highest priority, inserted at the front of the list. The << or push method adds to the end, lowest priority.
When you require a file it is loaded in, parsed, and evaluated, an operation which can take a small but measurable amount of time and will consume more memory to hold any class or method definitions inside the file, as well as any data such as string constants that may be declared. Loading in every single element of a library like ActiveRecord using require will require a considerable amount of memory, and this will import every database driver available, not just the ones that are actually used.
Ruby allows you to declare a class and a path to the file where it is defined, but with the advantage of not actually loading it in at that moment. This means that references to that class don't cause script errors in other parts of your application that make use of them.
You will often see declarations like this:
class Foo
# Declare the class Foo::Bar to be defined in foo/bar.rb
autoload(:Bar, 'foo/bar')
end
When using autoload you need to keep in mind that the class name is always defined within the scope of the module or class declaring it. In this example Bar is within Foo, or Foo::Bar using Ruby naming conventions.
When you make use of the Bar class, the foo/bar.rb file will be required. Think of it as creating a stub Bar class that transforms into the real class once it's actually exercised.
This is a great way of keeping a lot of options open, with many different modules ready to use, but without having to load everything into memory up front.
As for the third question, searchable documentation like APIDock will help you try and find more information on methods. The distinction between Ruby and Rails is often blurred, so you may have to check through both to be sure. Rails adds a lot of methods to core Ruby classes, so don't take the listing of methods available to be complete on either side. They work in conjunction.
Sometimes it pays to search for def methodname when trying to find out about where methodname originates, although this covers only conventional declarations. That method may be an alias from a mechanism like method_alias or may have been dynamically created using define_method, you can never really be sure until you dig around. At least 90% of the methods in Rails are declared the conventional way, though, so most of the time a simple search will yield what you want.

Resources