【问题标题】:Eager load all classes before routes.rb being loaded在加载 routes.rb 之前急切加载所有类
【发布时间】:2018-07-10 08:40:58
【问题描述】:

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

我的目标:根据定义的类动态创建合适的路由。

代码如下:

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

所以我的问题是:如何强制 rails 在加载 routes.rb 文件之前加载所有必要的类?

【问题讨论】:

  • 你用的是哪个版本的rails?
  • 我使用的是 rails 版本:5.0.2

标签: ruby-on-rails ruby metaprogramming


【解决方案1】:

我会向你推荐 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

【讨论】:

  • 这是一个了不起且非常有趣的职位。它确实帮助解决了我的问题,即使我决定采用更简单的解决方案,我也利用了它。然而 7stud 的回答是值得认可的,我真的很感激这个!
【解决方案2】:

此时在加载 routes.rb 文件时,Widget.available_types 为 空。

我没看到。一旦我将类的名称从 Widget::a 更改为 Widget::Dog 以防止出现一些错误,您的代码似乎对我来说很好。我还发现我必须在代码更改之间终止我的终端窗口,否则rails c 不会反映我的代码更改,有时会显示Widget.available_types 是空的。

如果我在routes.rb 中的namespace 块中添加一行:

  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 是空的。我不确定这是为什么。

【讨论】:

  • Lool,感谢您的出色调试!那时我错了,因为我尝试了rake routes 来查看路由是否正确生成。多亏了你,我似乎学到了很多;)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-04-01
  • 1970-01-01
  • 2017-10-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-09-30
相关资源
最近更新 更多