Ruby on rails 基于数据库模型的动态Rails路由

Ruby on rails 基于数据库模型的动态Rails路由,ruby-on-rails,ruby,heroku,routes,Ruby On Rails,Ruby,Heroku,Routes,所以我正在构建一个Rails站点,它需要基于两种不同类型的路由 我有一个语言模型和一个类别模型 因此,我需要能够转到语言路径/ruby以查看顶级ruby资源,还需要转到/books以查看所有语言的顶级书籍 我试过这样的路线 get '/:language', to: "top_voted#language" get '/:category', to: "top_voted#category" 问题是逻辑无法找出两者之间的区别,并在后端造成了一些冲突 我也试过这个 Language.all.ea

所以我正在构建一个Rails站点,它需要基于两种不同类型的路由

我有一个语言模型和一个类别模型

因此,我需要能够转到语言路径/ruby以查看顶级ruby资源,还需要转到/books以查看所有语言的顶级书籍

我试过这样的路线

get '/:language', to: "top_voted#language"
get '/:category', to: "top_voted#category"
问题是逻辑无法找出两者之间的区别,并在后端造成了一些冲突

我也试过这个

Language.all.each do |language|
  get "#{language.name}", to: "top_voted#language", :language => language.name
end

Category.all.each do |category|
  get "#{category.name}", to: "top_voted#category", :category => category.name
end

但是问题是我们部署的Heroku不允许在路由中调用数据库。有没有更简单的方法?我们需要能够以某种方式动态生成这些路由。

对于rails,两个路由get'/:language'和get'/:category'完全相同。 Rails路由器无法区分
/books
/ruby
。 在这两种情况下,rails只需在
routes.rb
中查找类似于
/something
的路由,它将选择第一个匹配项并将路由分派给指定控制器的操作

就你而言

所有带有
/something
格式的请求

将与


get'/:language',to:“top_voted#language”

这听起来像是架构问题。如果干净的URL对您很重要,下面是我如何设置的:

创建一个名为
Page
的新模型,该模型将属于特定资源(类别或语言)

现在,您可以使用单个路径进行路由,但仍然可以访问来自不同类的资源

路由器:

resources :pages, id: /[0-9a-z]/
控制器:

class PagesController
  def show
    @page = Page.find_by_path(params[:id])
  end
end
在视图中,为资源模型设置部分,然后在
页面/show
中呈现它们:

=render @page.resource

一个示例页面是
#
,可以在
/pages/ruby
上找到。你可能会将它路由到
/ruby
页面控制器
,但是你会严格限制你可以在应用程序其他地方使用的路由数量。

因为我晚了几个月,你可能已经想出了一些办法,但对于未来的人来说,约束可能就是你想要的。您可以设置一个lambda来根据请求对象进行决定,也可以设置一个类来实现路由器要调用的
matches?
方法


使用路由约束可以很好地解决该问题

使用路由约束 正如建议的那样,您可以通过路由约束检查路径是否属于某种语言或类别来定义路由约束

# config/routes.rb
# ...
get ':language', to: 'top_voted#language', constraints: lambda { |request| Language.where(name: request[:language]).any? }
get ':category', to: 'top_voted#category', constraints: lambda { |request| Category.where(name: request[:category]).any? }
顺序定义了优先级。在上面的示例中,如果一种语言和一个类别具有相同的名称,则该语言将获胜,因为它的路由是在类别路由之上定义的

使用Permalink模型 如果您想确保所有路径都是uniqe,一种简单的方法是定义
Permalink
模型并在那里使用验证

生成数据库表:
rails生成模型Permalink路径:字符串引用\u类型:字符串引用\u id:integer&&rails db:migrate

并在模型中定义验证:

class Permalink < ApplicationRecord
  belongs_to :reference, polymorphic: true
  validates :path, presence: true, uniqueness: true

end
使用此解决方案,路由文件必须如下所示:

# config/routes.rb
# ...
get ':language', to: 'top_voted#language', constraints: lambda { |request| Permalink.where(reference_type: 'Language', path: request[:language]).any? }
get ':category', to: 'top_voted#category', constraints: lambda { |request| Permalink.where(reference_type: 'Category', path: request[:category]).any? }
另外,对于在控制器中使用cancan gem和
load\u和\u Authorization\u资源的其他用户,作为一个旁注:在调用
load\u和\u Authorization\u资源之前,您必须通过permalink加载记录:

class Category < ApplicationRecord
  before_action :find_resource_by_permalink, only: :show
  load_and_authorize_resource

  private

  def find_resource_by_permalink
    @category ||= Permalink.find_by(path: params[:category]).try(:reference)
  end
end
类别
Rails无法区分
/books
/ruby
,它总是从
routes.rb
中选择第一个匹配的路由,在您的情况下,它是
get'/:language'
class Language < ApplicationRecord
  has_many :permalinks, as: :reference, dependent: :destroy

end
rails_category.permalinks.create path: 'rails'
rails_category.permalinks.create path: 'ruby-on-rails'
# config/routes.rb
# ...
get ':language', to: 'top_voted#language', constraints: lambda { |request| Permalink.where(reference_type: 'Language', path: request[:language]).any? }
get ':category', to: 'top_voted#category', constraints: lambda { |request| Permalink.where(reference_type: 'Category', path: request[:category]).any? }
class Category < ApplicationRecord
  before_action :find_resource_by_permalink, only: :show
  load_and_authorize_resource

  private

  def find_resource_by_permalink
    @category ||= Permalink.find_by(path: params[:category]).try(:reference)
  end
end