I have a rails application that runs a rails engine by explicitly declaring it in the Gemfile. I have a script set up to install the migrations from the engine via: rake railties:install:migrations and then migrates. When I run the script, the migrations are installed but when the migrations run I get a DuplicateMigration error. I investigated this further and saw that
Rails.application.paths['db/migrate'].to_a
resulted in:
["/src/db/migrate","/bundle/bundler/gems/my_engine-w8ejw9jf/db/migrate"]
Somehow the migration path from the bundle directory is getting added to the Rails application paths. Where does this happen? Is there a way I can prevent the migration paths from my bundle directory from being included in the application paths?
Okay I did some more digging and it turns out that this is actually being done in the Engine itself. In the engine.rb file there is some logic that changes the paths that are autoloaded. The core logic was as follows:
class Engine < ::Rails::Engine
isolate_namespace MyEngine
initializer :append_migrations do |app|
unless app.root.to_s.match root.to_s
config.paths["db/migrate"].expanded.each do |expanded_path|
app.config.paths["db/migrate"] << expanded_path
end
end
end
end
This was taking the expanded paths of the migration files, and shoving them into the paths config. This is why I was seeing /bundle/bundler/gems/mycoolgem-w8ejw9jf/db/migrate in the paths config.
Related
I have some Sidekiq workers that have a naming convention of like random_api_worker.rb and have the class defined as RandomAPIWorker and it has always worked up until Rails 6. In other cases, I have the classes starting off as class RandomAPIWorker although it's in a few subdirectories, such as app/workers/dir1/dir2/random_api_worker.rb
I have added config.autoloader = :classic to my application.rb file, but this seems to only do the trick if I'm running everything in development. The minute I flip the RAILS_ENV to production, then it starts complaining about worker names.
This brings me to a two questions:
Isn't the config.autoloader = :classic supposed to ignore this, or am I misunderstanding how this works?
Is there a Zeitwerk script available that could essentially upgrade classic worker names in a proper format/hierarchy?
If #1 is false, is there another way to keep my workers with their same names and not have to worry about renaming them to meet the requirements of Zeitwerk?
Here's my application.rb file:
require_relative 'boot'
require 'rails/all'
# Require the gems listed in Gemfile, including any gems
# you've limited to :test, :development, or :production.
Bundler.require(*Rails.groups)
module Vspm
class Application < Rails::Application
# Initialize configuration defaults for originally generated Rails version.
config.load_defaults 6.0
config.autoload = :classic
config.autoload_paths << Rails.root.join('app/workers/sampleworkers/**/')
# Settings in config/environments/* take precedence over those specified here.
# Application configuration can go into files in config/initializers
# -- all .rb files in that directory are automatically loaded after loading
# the framework and any gems in your application.
config.enable_dependency_loading = true
config.eager_load_paths += Dir["#{config.root}/lib/custom/**/"]
# Add images and subdirectories to asset pipeline
config.assets.paths << "#{Rails.root}/app/assets/images/severity_icons/"
end
Here's one error (complains about the caps in one of the class names):
ubuntu#c567d17a6700:~/myapp/app$ RAILS_ENV=production rails zeitwerk:check
Hold on, I am eager loading the application.
expected file app/services/pdf_generator.rb to define constant PdfGenerator
Here's the class name defined in that file:
# app/services/pdf_generator.rb
class PDFGenerator
After fixing this, the next error complains about the directory hierarchy not being in the worker's class name:
ubuntu#c567d17a6700:~/myapp/app$ RAILS_ENV=production rails zeitwerk:check
Hold on, I am eager loading the application.
expected file app/workers/shared/random_name_worker.rb to define constant Shared::RandomNameWorker
Here's how the class is mentioned in that file:
# app/workers/shared/random_name_worker.rb
class RandomNameWorker
Isn't the config.autoloader = :classic supposed to ignore this, or am I misunderstanding how this works?
this setting config Rails back to classic mode loader on the whole app, so of course it'll ignore structure name convenient.
you could setup autoload_paths with zeitwerk, your problem is the way you add nested workers directories to autoload_paths
# NOT THIS
config.autoload_paths << Rails.root.join('app/workers/sampleworkers/**/')
# SHOULD THIS
config.autoload_paths << Rails.root.join('app/workers/sampleworkers/')
# OR THIS (if you want to use `**`)
config.autoload_paths += Dir["#{config.root}/app/workers/**/**"]
# REPLACE
config.autoloader = :classic
# BY
config.load_defaults 6.0
Is there a Zeitwerk script available that could essentially upgrade classic worker names in a proper format/hierarchy?
i don't know whether there's a gem support that or not, i haven't seen so far, i do it manually, create module each sub directories, so your worker become something like this Api_Worker::Random
# app/worker/api_worker.rb
module ApiWorker
end
# app/worker/api_worker/random.rb
module ApiWorker
class Random
include Sidekiq::Worker
end
end
If #1 is false, is there another way to keep my workers with their same names and not have to worry about renaming them to meet the requirements of Zeitwerk?
As i said above, you could extend autoload_paths with zeitwerk
config.autoload_paths += Dir["#{config.root}/app/workers/**/**"]
and you still use the name RandomAPIWorker
Every time a controller or model tries to access a class on the /lib folder it says:
NameError (uninitialized constant 'current_controller':'class_name' did you mean 'something_else')
YES, I know the rails naming conventions and I am using it correctly. I have the code running in several other servers (Ubuntu & CentOS 6). It errors only on these 2 RedHat7.2 servers we have - same exact ruby/rails/gems on all servers. The error occurs with any library file I try to use. SELinux is disabled.
Ruby version 2.3.3; Rails version 5.1.0 (same on all servers)
Anyone have any ideas? Rails is suppose to automatically load those class files.
On rails < 5:
config/application.rb
module your_app
class Application < Rails::Application
# Settings in config/environments/* take precedence over those specified here.
# Application configuration should go into files in config/initializers
# -- all .rb files in that directory are automatically loaded.
config.autoload_paths += %W(#{config.root}/lib/path)
end
end
on rails >= 5
module your_app
class Application < Rails::Application
config.eager_load_paths << "#{Rails.root}/lib/path"
end
end
If you want rails to auto load your lib you have to place it under the app folder.
I am in the middle of developing a gem for rails and i stuck with this issue. My logic is I have a gem created, and the gem installed to my local machine. What I need is when I type gem_name --install, there is a file called test.rb should be copied to inside a rails project/ config/initializers/. The file to be copied is currently placed in a folder in my gem. I have tried
Dir.pwd
but it is not give me results as expected. Please find a solution for me and TIA..
For a rails gem you would use a generator together with a "template" file.
class FooGenerator < Rails::Generators::NamedBase
source_root File.expand_path("../templates", __FILE__)
def copy_initializer_file
copy_file "initializer.rb", "config/initializers/#{file_name}.rb"
end
end
This will would copy the file when the user of the gem runs rails generate foo.
Make sure to read through the rails guides sections on generators and creating engines as there are quite a few gotchas and conventions.
I know inside my Rails engine I can create an app/controllers/blah_controller.rb file and that controller will be accessible from my Rails app just like it was defined in the Rails app itself.
If I want the engine to expose a file into the root of the Rails application, where do I put that in the Rails engine? A basic gem would have a structure like:
my_engine
|_bin
|_lib
.gitignore
my_engine.gempspec
Gemfile
Rakefile
README.md
Do I just add the_file.rb to root of the gem? If so, how does Rails know to include it and not the other files in the gem root?
my_engine
|_bin
|_lib
.gitignore
my_engine.gempspec
Gemfile
Rakefile
README.md
the_file.rb # I want this file to be present in the Rails app root.
No Rails won't know how to load root path files.
The part of rails you are looking for is a railtie engine: http://api.rubyonrails.org/classes/Rails/Engine.html
I hope you mounted the engine to an application so you can debug. Within your app that has an engine, you can check eager loaded paths:
MyEngine::Engine.config.eager_load_paths
which returns an array of all loaded paths.
All you need to do is: to configure Engine to load the root path:
module MyEngine
class Engine < ::Rails::Engine
isolate_namespace MyEngine
config.eager_load_paths << File.expand_path('../../..', __FILE__)
end
end
Then create a dummy initializer in the Rails app, that requires your engine root file:
# /config/initializers/my_engine_dummy.rb
require 'your_engine_root_file.rb'
Place some methods inside your_engine_root_file.rb
p 'Welcome to my engine`
load rails console from your rails application and see the beautiful message
Assumption:
I assumed that you are using MyEngine as the namespace for the engine.
Suggestion:
Not to do that :) As many know that name collapse is a common thing in software development. So you might want to isolate your engine files behind the namespace.
Hope that helps
In my project, I have written few classes under lib folder but rails is not detecting those classes in production environment. I get the uninitalized Constant error.
I use Apache in the production environment and rails script/server in the development environment.
Is anything wrong with RAILS_ROOT environment? Can anyone suggest how to overcome this problem?
I am not sure about Rails, but you achieve that in Ruby by this: (it will work in rails too, but rails must be having some elegant way)
require File.join(File.dirname(__FILE__), "lib",'your_module_name')
include your_module_name
Try this in config/application.rb (I assume you have rails3)
config.load_paths += %W( #{config.root}/lib )
Update: Rails - why would a model inside RAILS_ROOT/lib not be available in production mode?
Ensure that the name of your file matches the name of the class or module defined in it, accounting for any directories.
ie:
lib/my_new_class.rb
class MyNewClass
end
Or if you have a directory hierarchy:
lib/my_files/my_module.rb
module MyFiles
module MyModule
end
end