在routes.rb加载之前,急切加载所有类

时间:2018-01-31 09:32:31

标签: ruby-on-rails ruby metaprogramming

我正在尝试进行一些元编程,以根据现有类动态定义路由。

我的目标:基于已定义的类动态创建正确的路由。

以下是代码:

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加载所有必需的类?

2 个答案:

答案 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为空。我不确定为什么会这样。