我正在尝试进行一些元编程,以根据现有类动态定义路由。
我的目标:基于已定义的类动态创建正确的路由。
以下是代码:
app/models/widget.rb
class Widget < ApplicationRecord
mattr_reader :available_types
class_variable_set(:@@available_types, {})
def self.type
available_types.key(name)
end
def self.register_type(key)
@@available_types[key] = self
end
end
app/models/widgets/a.rb
class Widget::a < Widget
register_type :a
end
config/environments/development.rb
...
config.eager_load = true
...
现在,当我打开控制台并输入:Widget.available_types
时,我立即看到:{ a: Widget::A }
所以现在我正在尝试创建路线:
config/routes.rb
namespace :widgets do
Widget.available_types.keys.each do |widget_type|
resources widget_type.to_s.pluralize
end
end
不幸的是,这不起作用。
此时加载routes.rb
文件时,Widget.available_types
为空。
将此更改添加到routes.rb
会使其正常运行,但这不是解决方案
config/routes.rb
Widget::A #manually call the class here. This makes code working but don't want that
namespace :widgets do
Widget.available_types.keys.each do |widget_type|
resources widget_type.to_s.pluralize
end
end
所以我的问题是:如何在加载routes.rb
文件之前强制rails加载所有必需的类?
答案 0 :(得分:4)
我建议您参加Michael Lang的博客
http://codeconnoisseur.org/ramblings/creating-dynamic-routes-at-runtime-in-rails-4
他的示例使用load
方法向Appplication.routes添加条目,然后使用reload
方法激活新路由。
class DynamicRouter
def self.load
ComingSoon::Application.routes.draw do
Page.all.each do |pg|
puts "Routing #{pg.name}"
get "/#{pg.name}", :to => "pages#show", defaults: { id: pg.id }
end
end
end
def self.reload
ComingSoon::Application.routes_reloader.reload!
end
end
答案 1 :(得分:1)
此时加载routes.rb文件时,Widget.available_types为 空。
我没有看到。我将类的名称从Widget::a
更改为Widget::Dog
后,您的代码似乎对我有用,以防止出现一些错误。我还发现我必须在代码更改之间终止我的终端窗口,否则rails c
不会反映我的代码更改,有时会显示Widget.available_types
为空。
如果我在namespace
中的routes.rb
块中添加一行:
namespace :widgets do
puts "[ME]: #{Widget.available_types}" #<==== HERE
Widget.available_types.keys.each do |widget_type|
resources widget_type.to_s.pluralize
end
end
然后执行rails s
,我在服务器窗口中看到以下输出:
~/rails_projects/myapp2018$ rails s
=> Booting Puma
=> Rails 5.1.4 application starting in development
=> Run `rails server -h` for more startup options
[ME]: {:dog=>Widget::Dog (call 'Widget::Dog.connection' to establish a connection)}
...
...
这表明Widgets.available_types
不是空的。事实上,如果我在浏览器中输入以下网址:
http://0.0.0.0:3000/widgets/dogs/show
我被带到了views/widgets/dogs/show.html.erb
视图。如果我在routes.rb
中注释掉您的命名空间块,我会收到路由错误。我认为这表明您的代码成功创建了动态路由。
如果我终止终端窗口,然后执行rails c
,我也会看到Widgets.available_types
不为空:
$ rails c
[ME] Widgets.availabe_types: {:dog=>Widget::Dog (call 'Widget::Dog.connection' to establish a connection)}
Running via Spring preloader in process 22753
Loading development environment (Rails 5.1.4)
2.4.0 :001 >
另一方面,如果我这样做:
$ rake routes
我明白了:
[ME] Widgets.availabe_types: {}
Prefix Verb URI Pattern Controller#Action
GET /support(/*all)(.:format) redirect(301, path: /contact/%{all})
GET /contact(/*path)(.:format) contacts#show
这表明Widgets.available_types
为空。我不确定为什么会这样。