Deprecation warning mocks won't be added automatically to load paths - ruby-on-rails

After upgrading to Rails 3 I get this deprecation warning:
DEPRECATION WARNING: "Rails.root/test/mocks/test" won't be added automatically to load paths anymore in future releases.
So how should this be implemted in Rails 3?

Rails just checks for the existence of the mocks/test directory and spits out that warning if it exists. Here is what the code looks like from rails/railties/lib/rails/application/configuration.rb:
if File.exists?("#{root}/test/mocks/#{Rails.env}")
ActiveSupport::Deprecation.warn "\"RAILS_ROOT/test/mocks/#{Rails.env}\" won't be added " <<
"automatically to load paths anymore in future releases"
paths.mocks_path "test/mocks", :load_path => true, :glob => Rails.env
end
So it looks like this message will remain until they deprecate it.
If you need this path in your load path in the future, I think you just do something like this in config/application.rb (notice the comments taken directly from the config/application.rb template):
# Add additional load paths for your own custom dirs
config.load_paths += %W( #{config.root}/test/mocks/#{Rails.env} )
I haven't tried it but this should help!

Related

Ruby on Rails: configure web_console in initializer

I am using the web_console gem and I would like to add some IPs to the whitelist. For reasons that would probably go to far to explain, can't simply add something to the config/application.rb or config/environments/development.rb. However I can create an initializer config/initializers/.
I simple tried this in config/initializers/99-webconsole.rb, but while the file is loaded (--> debug message is shown), the web console does not seem to pick up my settings.
Rails.application.configure do
config.web_console.whitelisted_ips = '10.10.0.0/16'
p "Debug: this is loaded."
end
I assume it's related to some kind of race condition? Providing the same line in config/environments/development.rb works, but as said, I sadly can not change that file.
Based on this code https://github.com/rails/web-console/blob/e3dcf4c588af526eafcf1ce9413e62d846599538/lib/web_console/railtie.rb#L59
maybe there is a code in your initializer that configuring config.web_console.permissions, so your whitelisted_ips config is ignored
whitelisted_ips is also deprecated
and have you checked that you are using v4.2.0, the permissions was buggy and fixed by this commit https://github.com/rails/web-console/commit/6336c89385b58e88b2661ea3dc42fe28651d6296

Accessing modules in Rails lib folder

There are probably hundreds of these questions on here but I haven't been able to get one to work yet. I'm using Rails 6 and I always have trouble writing custom modules.
I have a file for creating email tokens: lib/account_management/email_token.rb
module AccountManagement
module EmailToken
def create_email_token(user)
....
end
end
end
Then in my controller I have tried all kinds of things:
require 'account_management/email_token'
include AccountManagement::EmailToken
Calling it with: create_and_send_email_token(user)
At one point I added config.eager_load_paths << Rails.root.join('lib/account_management/') but I didn't think you still had to do that.
Whenever I try to call the controller action I get one of a few error messages:
*** NameError Exception: uninitialized constant Accounts::SessionsController::EmailToken
#this happens if I try to manually send through the rails console
(although inside of a byebug I can call require 'account_management/email_token' and it returns
true.
Most commonly I get:
NoMethodError (undefined method `create_email_token' for #<Accounts::SessionsController:0x00007ffe8dea21d8>
Did you mean? create_email):
# this is the name of the controller method and is unrleated.
The simplest way to solve this is by placing your files in app/lib/account_management/email_token.rb. Rails already autoloads any subdirectory of the app folder*.
/lib has not been on the autoload paths since Rails 3. If you want to add it to the autoload paths you need to add /lib not /lib/account_management to the autoload/eager loading paths. In Zeitwerk terms this adds a root where Zeitwerk will index and resolve constants from.
config.autoload_paths += config.root.join('lib')
config.eager_load_paths += config.root.join('lib')
Note that eager_load_paths is only used when eager loading is turned on. Its turned off by default in development to enable code reloading.
/lib is added to $LOAD_PATHso you can also manually require the file with:
require 'account_management/email_token'
See:
Autoloading and Reloading Constants (Zeitwerk Mode)
Rails #37835

Zeitwerk error on devise mailer in production environment

I have Rails 6, my preview class located in
mailer/previews/devise_mailer_preview.rb:
class DeviseMailerPreview < ActionMailer::Preview
...
end
And when I run application locally, everything is going fine, I can see my email previews on http://localhost:3000/rails/mailers/devise_mailer/confirmation_instructions address. But now Im trying to deploy application on server, and found that when I run bundle exec rails c production, I got the error:
/home/deploy/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/zeitwerk-2.3.0/lib/zeitwerk/loader/callbacks.rb:17:in
`on_file_autoloaded': expected file
/home/deploy/project/releases/20200627024908/app/mailer/previews/devise_mailer_preview.rb
to define constant Previews::DeviseMailerPreview, but didn't
(Zeitwerk::NameError)
After that I've checked locally RAILS_ENV=production rails c, and got same.
If I will rename DeviseMailerPreview class to Previews::DeviseMailerPreview, it will be broken and I cannot see emails on development, because Rails 6, accordingly to docs, expect exactly that name.
More of that, I've found in this article, that zeitwerk can be configured with autoload_paths param to avoid ruby's NameError. I found that I have it my config/application.rb:
config.load_defaults 6.0
Anyway I tried to add same row in my config/environments/production.rb file, but it didn't help.
What am I doing wrong and how can I fix it? Thanks in advance!
Add preview_path to the autoload_paths and zeitwerk will expect DeviseMailerPreview constant to be defined.
# development.rb
config.action_mailer.preview_path = Rails.root.join("app/mailers/previews")
# application.rb
config.autoload_paths << Rails.root.join("app/mailers/previews")
Your Mailer preview file is located in mailer/previews/devise_mailer_preview.rb, so I'm assuming it's full path is app/mailer/previews/devise_mailer_preview.rb
The docs says
In the above example, the preview class for UserMailer should be named UserMailerPreview and located in test/mailers/previews/user_mailer_preview.rb
So put your devise_mailer_preview.rb file to test/mailers/previews/devise_mailer_preview.rb
or in your config/application.rb add this line and restart:
config.action_mailer.preview_path = "#{Rails.root}/app/mailers/previews"
Actually the answer was in the docs itself.
In Rails 6, previews are added to the autoload paths only if options.show_previews is true, which is not by default in the production environment. See the source code here.
The reason for this is that previews are supposed to be an aid for development, and they are generally not something you want to be able to look at in production.
However, you can set that flag to true in production if you want.
There's another derivative: By storing the previews under app/mailers, Rails is going to eager load them because app/mailers is in the autoload paths. If app/mailers/previews is not in the autoload paths, eager loading will fail due to the namespace mismatch. Either you have them enabled in all environments, or else is better to have them in a separate location, like the default.

Can I force a namespace for Rails autoloading without nesting in a folder?

Is there a way to tell Rails that all files in a certain folder are contained in a certain namespace?
Ie.
I have a file bar.rb in app/foo. Rails will assume this file defines Bar, but instead I want this file to define Foo::Bar.
I know I can achieve this by adding my root to Rails' autoload paths, but that isn't a real solution. Is there any other way I can tell Rails that all files within app/foo reside in the Foo namespace?
EDIT: File tree for clarification
app
assets
controllers
models
foo
bar.rb
quux.rb
I would like to be able to define Foo::Bar and Foo::Quux in respectively bar.rb and quux.rb, while also using Rails autoloading. Without having to resort to the tree structure as below:
app
assets
controllers
models
foo
foo
bar.rb
quux.rb
You can autoload files with namespaces corresponding to directories inside /app by adding /app to your autoload paths in your application config.
# config/application.rb
config.autoload_paths << "#{config.root}/app"
I'm not sure whether or not this is what the author of the question meant by
I know I can achieve this by adding my root to Rails' autoload paths, but that isn't a real solution.
But this is definitely a real solution and the correct one. Doing this is fine and not at all unusual in my experience.
There are only minor side effects which are the cost of adopting the inconsistent naming conventions you want. If you refer to an undefined constant that matches the name of another directory in the app you'll get a slightly different error message.
# Models::Foo => LoadError (Unable to autoload constant Models::Foo, expected /app/models/foo.rb to define it)
But if you have existing namespaces that match directories in app loading will still work fine. Here's what Rails is doing:
It takes the paths added to config.autoload_paths and adds them to the defaults (the directories under app: app/models, app/controllers, app/foo etc). Then when a constant is referenced that is not already loaded Rails proceeds through those paths, looking for paths that match the constants. So when you reference Foo::Bar it looks for app/models/foo/bar.rb, app/controllers/foo/bar.rb etc. until it finds a file that defined Foo::Bar. All we're doing is adding app/foo/bar.rb to that lookup.
Rails doesn't assume namespace, it assumes path to the source file depending on a namespace, so:
$ cat app/foo/bar.rb
module Foo
class Bar
def bar
puts "i'm here"
end
end
end
$ rails c
2.2.0 :007 > b=Foo::Bar.new
=> #<Foo::Bar:0x00000005272770>
2.2.0 :008 > b.bar
i'm here
=> nil
You can try adding the folder you Rail's autoload paths configuration
# config/application.rb
# Rails 4
config.autoload_paths << Rails.root.join("app", "foo")
# Rails 5+
config.eager_load_paths << Rails.root.join("app", "foo")

Adding a directory to the load path in Rails?

As of Rails 2.3, what's the right way to add a directory to the load path so that it hooks into Rails' auto-reloading mechanisms?
The specific example I'm thinking of is I have a class that has several sub-classes using STI and I thought it would be a good idea to put them in a sub-directory rather than clutter the top-level. So I would have something like:
#app/models/widget.rb
class Widget < ActiveRecord::Base
add_to_load_path File.join(File.dirname(__FILE__), "widgets")
end
#app/models/widgets/bar_widget.rb
class BarWidget < Widget
end
#app/models/widgets/foo_widget.rb
class FooWidget < Widget
end
It's the add_to_load_path method that I'm looking for.
In the current version of Rails (3.2.8), this has been changed in the application.rb file.
The code is currently commented out as:
# Custom directories with classes and modules you want to be autoloadable.
# config.autoload_paths += %W(#{config.root}/extras)
Will need to update the autoload_paths value. Attempting to modify the the former load_paths variable causes this error.
/configuration.rb:85:in `method_missing': undefined method `load_paths' for #<Rails::Application::Configuration:0xac670b4> (NoMethodError)
for an example, for each path to add to autoload_paths config, add a line similar to the following:
config.autoload_paths += %W(#{config.root}/app/validators)
config.autoload_paths accepts an array of paths from which Rails will autoload constants. Default is all directories under app.
http://guides.rubyonrails.org/configuring.html
From commentor (hakunin) below:
If the directory is under app/, you don't need to add it anywhere, it should just work by default (definitely in 3.2.12). Rails has eager_load_paths that acts as autoload_paths in development, and eager load in production. All app/* directories are automatically added there.
For older versions of Rails:
You can do this in your environment.rb config file.
config.load_paths << "#{RAILS_ROOT}/app/widgets"
--
For Rails 3, see answers bellow
In Rails 5 you don't have to explicitly load folders from within the app directory anymore. All folders placed inside are directly available. You don't have to touch any of the config files. But it seems as if there are some issues with Spring.
The new workflow therefore is:
create a new folder and class inside the /app directory
run spring stop on the command line
check the autoload-paths with bin/rails r 'puts ActiveSupport::Dependencies.autoload_paths' on the command line. The new folder should now be listed.
run spring start on the command line
In Rails 3, you can set this in config/application.rb, where this sample is provided by default:
# Add additional load paths for your own custom dirs
# config.load_paths += %W( #{config.root}/extras )
On Rails 5 you need to add the following code to environment.rb:
# Add the widgets folder to the autoload path
Rails.application.configure do
config.autoload_paths << "#{Rails.root}/app/widgets"
end
Another update for rails 3 -- activesupport 3.0.0:
Instead of:
ActiveSupport::Dependencies.load_paths << "#{RAILS_ROOT}/app/widgets"
You may need to do this:
ActiveSupport::Dependencies.autoload_paths << "#{RAILS_ROOT}/app/widgets"
I found I needed to do this after config block-- no access to config object anymore.
This did the trick
ActiveSupport::Dependencies.load_paths << "#{RAILS_ROOT}/app/widgets"
In config/application.rb add config.autoload_paths << "#{config.root}/models/widgets".
File should look like this:
module MyApp
class Application < Rails::Application
config.autoload_paths << "#{config.root}/models/widgets"
end
end
I know this works for Rails 4 and 5. Probably others as well.
If you want to add multiple directories:
config.autoload_paths += Dir[Rails.root / "components/*/app/public"]
(this is an example for packwerk autoload)

Resources