如何直接创建到范围的链接/路由?

时间:2014-10-27 04:39:32

标签: ruby-on-rails ruby-on-rails-4 routing

所以我有一个Post模型,它有这些范围:

  scope :unconfirmed, -> { where( status: "unconfirmed") }    
  scope :corroborated, -> { where( status: "corroborated") }
  scope :confirmed, -> { where( status: "confirmed") }

我想要做的是当某人前往posts/confirmed时,它会显示所有确定范围的帖子。

最优雅的方法是什么?另外,如何在routes.rb中为每个范围创建路径?

修改1

我知道一种方法是在我的posts控制器中为每个可枚举的范围创建一个动作(即unconfirmed等)。

但我宁愿在我的索引操作中完成所有这些 - 所以我正在渲染并使用我的索引视图。

我已尝试在Post#Index

中执行此操作
if params[:unconfirmed]
  @posts = Post.published.unconfirmed
end

但这只是给我一个路由错误。

这是错误:

Started GET "/unconfirmed" for 127.0.0.1 at 2014-10-27 02:29:51 -0500
Processing by PostsController#show as HTML
  Parameters: {"id"=>"unconfirmed"}
  Post Load (3.0ms)  SELECT  "posts".* FROM "posts"  WHERE "posts"."publication_status" = 1 AND "posts"."slug" = 'unconfirmed'  ORDER BY "posts"."id" ASC LIMIT 1
  Post Load (1.7ms)  SELECT  "posts".* FROM "posts" INNER JOIN "friendly_id_slugs" ON "friendly_id_slugs"."sluggable_id" = "posts"."id" AND "friendly_id_slugs"."sluggable_type" = 'Post' WHERE "posts"."publication_status" = 1 AND ("friendly_id_slugs"."sluggable_type" = 'Post' AND "friendly_id_slugs"."slug" = 'unconfirmed')  ORDER BY "posts"."id" ASC LIMIT 1
Completed 404 Not Found in 28ms

ActiveRecord::RecordNotFound - ActiveRecord::RecordNotFound:
  friendly_id (5.0.4) lib/friendly_id/finder_methods.rb:23:in `find'
   () myapp/controllers/posts_controller.rb:78:in `set_post'

set_post方法是:

  private
    # Use callbacks to share common setup or constraints between actions.
    def set_post
      @post = Post.published.find(params[:id])      
    end

我正在使用friendly_id作为我的网址。

这是我的routes.rb

  get 'posts/:id' => redirect("/%{id}")
  get '/:friendly_id', to: 'posts#show'
  get 'posts/:friendly_id', to: 'posts#show'

修改2

我仍然收到此错误。我相信是这样的,因为每当我尝试/confirmed或任何其他状态时,这就是我的日志:

Started GET "/confirmed" for 127.0.0.1 at 2014-10-31 18:28:55 -0500
  ActiveRecord::SchemaMigration Load (2.2ms)  SELECT "schema_migrations".* FROM "schema_migrations"
Processing by PostsController#show as HTML
  Parameters: {"friendly_id"=>"confirmed"}
Completed 404 Not Found in 58ms

遵守此路由规则:

  get '/:friendly_id', to: 'posts#show'
  get 'posts/:friendly_id', to: 'posts#show'

即使我在路由文件的末尾放置了resources post的推荐路由规则,它仍然会这样做,例如:

  resources :posts do
    collection do
      get 'confirmed' => 'posts#index', status: 'confirmed'
      get 'unconfirmed' => 'posts#index', status: 'unconfirmed'
      get 'corroborated' => 'posts#index', status: 'corroborated'
    end
  end

  root to: "posts#index"

如何让状态路由不被重定向到posts#index操作?

编辑3

查看我的完整routes.rb

Rails.application.routes.draw do

  %w[privacy terms].each do |page|
    get page, controller: 'info', action: page
  end

  resources :locations      
  devise_for :users, :path_names => { :sign_up => "register", 
                                      :sign_in => "login", 
                                      :sign_out => "logout",
                                                                            :settings => "settings" },
                    :controllers => { :confirmations => "confirmations" }

  devise_scope :user do
    get "login", :to => "devise/sessions#new"
    get "register", :to => "devise/registrations#new"
        get "settings", :to => "devise/registrations#edit"
    delete "logout",   :to => "devise/sessions#destroy"    
  end

  resources :posts do
    collection do
      get 'confirmed' => 'posts#status', status: 'confirmed'
      get 'unconfirmed' => 'posts#status', status: 'unconfirmed'
      get 'corroborated' => 'posts#status', status: 'corroborated'
    end
  end

  get 'posts/:id' => redirect("/%{id}")
  get '/:friendly_id', to: 'posts#show'
  get 'posts/:friendly_id', to: 'posts#show'

  # This supports legacy URLs e.g:
  # http://www.example.com/rbt/26766-algaj-pays-tribute-to-the-honourable-roger-clarke.html   

  get '/rbt/:name', to: redirect {|path_params, _| "/#{path_params[:name].gsub(/^\d+\-/, '')}" } 
  get ':name',      to: 'posts#show'

  root to: "posts#index"

end

编辑4

问题的最后一点是问题的原始前提,我如何创建指向新创建路径的链接。

棘手的一点是我想根据状态动态创建link_path。

例如,作为新路线规则的结果,我现在有confirmed_path, corroborated_path, unconfirmed_path

但是,我在我的视图中显示它们是这样的:

<span class="post-status status label<%=render partial: "shared/color", locals: {post: post.status }%>"><%= post.status.try(:upcase) %></span>

我试过这样做:

<span class="post-status status label<%=render partial: "shared/color", locals: {post: post.status }%>"><%= link_to post.status.try(:upcase), post.status.path %></span>

但这给了我一个undefined method path错误。

在正常情况下,我只是进行字符串插值,Ruby会渲染当前字符串。鉴于Ruby应该解析这条路径,我怀疑这会起作用。

那么如何让该视图根据confirmed_path, corroborated_path, unconfirmed_path动态自动生成post.status

1 个答案:

答案 0 :(得分:1)

如你所见,你在路线上迷路了。此处调用posts/confirmed将改为posts#show而不是posts#index方法。原因是动作包从上到下给出了路由的优先级,因为你的路由定义如下:

resources 'posts'
get 'posts/:id' => redirect("/%{id}")
get '/:friendly_id', to: 'posts#show'
get 'posts/:friendly_id', to: 'posts#show'

所有posts/unconfirmedposts/confirmed等都不会对索引方法产生影响,因为它会与posts#show匹配,然后与get 'posts/:friendly_id', to: 'posts#show'匹配。

现在,即使您将所有get路由移至顶部,也不会将其转移到您稍后可能要定义的其他方法,因为所有posts/unconfirmedposts/confirmed等都将与get 'posts/:id' => redirect("/%{id}")匹配,并将重定向为/confirmed/unconfirmed等。

推荐的方法是为单个路线设置单独的方法,因为将来您可能希望确认的帖子与确认的帖子具有不同的行为。为此,您的路线将如下所示:

resources :posts do
  collection do
    get 'confirmed'
    get 'unconfirmed'
    get 'corroborated'
  end
end

运行$ rake routes|grep posts会给出:

            confirmed_posts GET    /posts/confirmed(.:format)                          posts#confirmed
          unconfirmed_posts GET    /posts/unconfirmed(.:format)                        posts#unconfirmed
         corroborated_posts GET    /posts/corroborated(.:format)                       posts#corroborated
                      posts GET    /posts(.:format)                                    posts#index
                            POST   /posts(.:format)                                    posts#create
                   new_post GET    /posts/new(.:format)                                posts#new
                  edit_post GET    /posts/:id/edit(.:format)                           posts#edit
                       post GET    /posts/:id(.:format)                                posts#show
                            PATCH  /posts/:id(.:format)                                posts#update
                            PUT    /posts/:id(.:format)                                posts#update
                            DELETE /posts/:id(.:format)                                posts#destroy

然后您创建单独的方法,即confirmedunconfirmedcorroborated等。

但是,您始终可以将所有这些路线指向这样的路线(不建议这样做):

resources :posts do
  collection do
    get 'confirmed' => 'posts#status', status: 'confirmed'
    get 'unconfirmed' => 'posts#status', status: 'unconfirmed'
    get 'corroborated' => 'posts#status', status: 'corroborated'
  end
end

然后在PostsController

def status
  @posts = Post.published.where(status: params[:status])
end

更新:将您的routes.rb更改为此 -

Rails.application.routes.draw do

  %w[privacy terms].each do |page|
    get page, controller: 'info', action: page
  end

  resources :locations      
  devise_for :users, :path_names => { :sign_up => "register", 
                                      :sign_in => "login", 
                                      :sign_out => "logout",
                                                                            :settings => "settings" },
                    :controllers => { :confirmations => "confirmations" }

  devise_scope :user do
    get "login", :to => "devise/sessions#new"
    get "register", :to => "devise/registrations#new"
        get "settings", :to => "devise/registrations#edit"
    delete "logout",   :to => "devise/sessions#destroy"    
  end

  resources :posts
  get '/confirmed' => 'posts#status', status: 'confirmed', as: :confirmed
  get '/unconfirmed' => 'posts#status', status: 'unconfirmed', as: :unconfirmed
  get '/corroborated' => 'posts#status', status: 'corroborated', as: :corroborated
  get 'posts/:id' => redirect("/%{id}")
  get '/:friendly_id', to: 'posts#show'
  get 'posts/:friendly_id', to: 'posts#show'

  # This supports legacy URLs e.g:
  # http://www.example.com/rbt/26766-algaj-pays-tribute-to-the-honourable-roger-clarke.html   

  get '/rbt/:name', to: redirect {|path_params, _| "/#{path_params[:name].gsub(/^\d+\-/, '')}" } 
  get ':name',      to: 'posts#show'

  root to: "posts#index"

end

并确保status中有PostsController方法:

def status
  # your code for fetching posts goes here!
  render text: params[:status] # this is to show an example that `status` is set properly
end