I have a Rails Mountable Engine called Blog.
Inside the module, I have a method called root_path. The module loads the root path of the engine.
module Blog
def self.root_path
Engine.routes.url_helpers.root_path
end
end
Inside one of the javascript assets of the Rails engine, I load the root url of the engine using erb syntax. Like the following line:
url = <%= Blog.root_path %>
When I run, rake assets:precompile, inside my app, I get an error saying the module does not contain such method. Like it isn't loading the engine library before precompiling the assets.
The error is:
undefined method `root_path' for #< Module:0xc185e14>
Rails engines provide their routing helpers through a routing proxy. You don't need to define root_path methods like this.
Instead, call the method which is your engine's name and then the routing helper on it like this:
blog.root_path
For more information, read the Engines Guide.
Even though Ryan's answer was helpfull, it wasn't the reason I was getting the error.
The reason was that I had set sep up initialize_on_precompile to false in my config/application.rb, so my app wasn't being started.
The Rails guides clearly states:
*For faster asset precompiles, you can partially load your application by setting config.assets.initialize_on_precompile to false in config/application.rb, though in that case templates cannot see application objects or methods*
Related
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
I am creating two engines. Engine Two builds on Engine One. I am using both of these Engines in an application I'm building. I used the class_eval Engine overriding technique mentioned in the rails guide.
# app/models/active_stix/tool_override.rb
ActiveStix::Tool.class_eval do
# Model override code here
end
The override is required from lib/engine_name/engine.rb as follows
initializer "active_attack.update_stix" do |app|
engine_root = File.expand_path("../../..", __FILE__)
Dir.glob(engine_root + "/app/overrides/**/*_override*.rb").each do |c|
require_dependency(c)
end
end
Everything works as expected in development, but when I run rails zeitwerk:check or try to deploy the application to Heroku, I get the following error
expected file ~/.rvm/gems/ruby-2.7.0/gems/active_attack-0.1.14/app/overrides/models/active_stix/tool_override.rb to define constant Models::ActiveStix::ToolOverride
This seems to be related to Rails issue 36100, but that issue seems to describe how the main Rails app can override Engine models. It doesn't seem to work for one Engine overriding another Engine's models.
Answered in Github Issue
Rails.autoloaders.main.ignore(absolute/path/to/app/overrides) ignores the overrides directory.
config.autoloader = :classic uses previous Rails autoloader instead of zeitwerk.
I've been trying to set up Forem (a Rails 4 forum engine) using a guide and original docs.
Most things work, but I'm getting route errors. In my application.erb I have this route in a link_to:
topic_path(u)
The guide recommends that I preface this with my application name so my routes won't conflict with Forem's routes, so I did that as so:
H2le.topic_path(u)
(H2le is the application name set in application.rb)
However, this errors out:
"undefined method `topic_path' for H2le:Module"
Am I not setting the application name properly?
The problem was my being a Ruby newb and the guide I was following perhaps not being super explicit. It recommended to namespace the links like:
main_app.path
And I interpreted main_app to be a placeholder for my app name. Well, wrong. main_app is a built-in helper function, so it should literally just say main_app. I fixed this, and everything worked.
I'm trying to get custom scaffolding working from my engine.
I followed some tutorial on customizing Rails 3.2 scaffolding in a normal Rails App and put my customized templates in the engines /lib/templates/erb/scaffold directory but they don't get picked up by the app that includes the engine. Any suggestions?
Update:
I also tried to override the Rails ScaffoldGenerator's source_path and tried some other paths to put my template in, like:
lib/rails/generators/erb/scaffold/templates
zarazan's answer got me most of the way there, but there are a couple of things wrong with it. Here's what worked for me:
class Engine < Rails::Engine
config.generators do |g|
g.templates.unshift File::expand_path('../../templates', __FILE__)
end
end
Note that this goes in the generators section, not app_generators, and that the path is slightly different.
Also, I think the correct path to store your templates is lib/templates/erb/scaffold, optionally replacing erb with whatever language you are using (like haml or slim.) I know this works for slim. The file names are {_form,edit,index,new,show}.html.erb.
In the file that you declare your engine use this command:
class Engine < Rails::Engine
config.app_generators do |g|
g.templates.unshift File::expand_path('../templates', __FILE__)
end
end
It should shift the preference of what template folder Rails uses by default.
Now just put the template files in lib/templates/erb/scaffold/template_name.erb
Where template_name is one of the following: _form.html.erb, edit.html.erb, index.html.erb, new.html.erb, show.html.erb
Once you include the gem you should be able to use the rails generate scaffold command as normal.
Here is an example of an engine that overrides the default scaffolding in rails:
https://github.com/brocktoncg/gemboree
This is where the template directory is located:
https://github.com/brocktoncg/gemboree/tree/master/lib/templates/erb/scaffold
Are you talking about a controller template? Than you are using the wrong directory. Save your template at
lib/templates/rails/scaffold_controller/controller.rb
Have a look at http://xyzpub.com/en/ruby-on-rails/3.2/templates.html for an example.
I am having an issue with the asset pipeline, I already have a resource / controller called assets. So i have changed assets.prefix option to "/externals".
config.assets.prefix = '/externals'
This simple dose not work unless i remove:
resources: assets
Then all works as expected.
I am not sure how to write a test to prove this but i have created a app to showcase it.
https://github.com/nodrog/asset-pipeline-issue
If you run the app, and visit '/products' all will work, then go to the routes file and change the variable create_bug to true.
We have looked into https://github.com/rails/rails/blob/master/actionpack/lib/sprockets/helpers/rails_helper.rb, and then added a debugger to the asset_path method.
This method is not called, if you but a debugger in the javascript_include_tag method. And run method(:asset_path).source_location, it tells you it is calling the method from default routes not from the sprockets helper.
Any help would be greatly appreciated...
UPDATE:
I reported this to the rails people, and they fixed it. The fix is now in the master branch.
https://github.com/rails/rails/issues/3643#issuecomment-2775938