Devise脚手架生成器source_root重复设置问题求助
问题背景
构建Devise脚手架生成器时,取消注释source_root=File.expand_path("../../templates/controllers", __FILE__)后测试失败,控制器动作报错:
Could not find "confirmations_controller.rb" in any of your source paths. Your current source paths are:
/home/jackparsons210/devise/lib/generators/active_record/templates
.system temporary path is world-writable: /tmp
/tmp is world-writable: /tmp
[repeated thrice]
无法重复设置source_root,询问是否必须通过File命令手动处理模板。
解决方案
不需要手动处理文件,有两种更优雅的方式解决source_root冲突问题:
方法1:调用template时使用模板绝对路径
直接在template方法中传入模板文件的绝对路径,无需修改全局source_root,避免和原有ActiveRecord模板路径冲突:
def create_controllers # 获取控制器模板的根目录绝对路径 controller_templates_root = File.expand_path("../../templates/controllers", __FILE__) @scope_prefix = table_name.blank? ? '' : (table_name.camelize + '::') CONTROLLERS.each do |name| template File.join(controller_templates_root, "#{name}_controller.rb"), "app/controllers/#{table_name}/#{name}_controller.rb" end end
方法2:临时切换source_root后恢复
在create_controllers方法内先保存原有source_root,临时切换到控制器模板路径,完成模板生成后再恢复原路径,不影响其他方法使用原source_root:
def create_controllers # 保存原有source_root original_source_root = self.class.source_root # 临时切换到控制器模板路径 self.class.source_root = File.expand_path("../../templates/controllers", __FILE__) @scope_prefix = table_name.blank? ? '' : (table_name.camelize + '::') CONTROLLERS.each do |name| template "#{name}_controller.rb", "app/controllers/#{table_name}/#{name}_controller.rb" end # 恢复原有source_root self.class.source_root = original_source_root end
问题原因
source_root是类级别的配置,直接全局修改会覆盖掉生成器原本的ActiveRecord模板路径(../templates),导致依赖该路径的方法(比如copy_devise_migration)无法找到迁移模板,同时控制器模板路径也没正确生效,所以出现找不到控制器模板的错误。上面两种方法都避免了全局修改source_root,既保证控制器模板能被找到,也不影响原有功能。
修改后的完整生成器代码片段
替换原create_controllers方法即可,其他代码保持不变:
# frozen_string_literal: true require 'rails/generators/active_record' require 'generators/devise/orm_helpers' require "generators/devise/controllers_generator.rb" module ActiveRecord module Generators class ScaffoldGenerator < ActiveRecord::Generators::Base argument :attributes, type: :array, default: [], banner: "field:type field:type" class_option :primary_key_type, type: :string, desc: "The type for primary key" include Devise::Generators::OrmHelpers source_root File.expand_path("../templates", __FILE__) # ... 其他原有方法保持不变 ... CONTROLLERS = %w(confirmations passwords registrations sessions unlocks omniauth_callbacks).freeze # 使用方法1的实现 def create_controllers controller_templates_root = File.expand_path("../../templates/controllers", __FILE__) @scope_prefix = table_name.blank? ? '' : (table_name.camelize + '::') CONTROLLERS.each do |name| template File.join(controller_templates_root, "#{name}_controller.rb"), "app/controllers/#{table_name}/#{name}_controller.rb" end end end end end
内容的提问来源于stack exchange,提问作者jack parsons

