简体   繁体   中英

Rails Sti: single path, different controller

Have STI classes:

class Page < ActiveRecord::Base
  belongs_to :user
end

class FirstTypePage < Page
end

class SecondTypePage < Page
end

Controllers for each class,

class PageController < AplicationCorroller
end

class FirstTypePageController < PageController
end

class SecondTypePageController < PageController
end

And routings:

resources :user
  resource :page
end

How to handle FirstTypePage by FirstTypePageController, SecondTypePage by SecondTypePageController on single path?

ie

user/1/page/2 is handled by: FirstTypePageController if "page 2" type is "FirstTypePage", and by SecondTypePageController if "page 2" type is "SecondTypePage" ?

UPDATE: My solution:

  match 'user/:user_id/page/:action',
    :controller=>'page/first_type_page',
    :constraints=>PageConstraints.new('FirstTypePage')
  match 'user/:user_id/page/:action',
    :controller=>'page/second_type_page',
    :constraints=>PageConstraints.new('SecondTypePage')

class PageConstraints

  @@cache ||= {}

  def initialize o_type
    #@mutex = Mutex.new
    @o_type = o_type
  end

  def matches?(request)
    user_id = request.params[:user_id]
    #add Mutex lock here
    unless page_type = @@cache[user_id]
      page_type = User.find(user_id).do_some_magik_to_suggest_type
      @@cache[page_id] = page_type
      @@cache.shift if @@cache.size > 1000
    end
    page_type == @o_type
  end

end

I think this solution will work fast on a small amount of page types, and we can manage memory size, used for routings on a large amount of pages

I can see one option to do that - preload all pages in the routes.rb and define special routes for each page.

resources :users do |user|
  Page.all do |page|
    if page.first_type?
      # ... routes to first_type_page_controller
    else
      # ...
  end
end

Another solution could be to use strategy patter in the PageController (no need to use FirstTypePageController and other).

pages_controller.rb:

before_filter :choose_strategy

def show
  @strategy.show
end

private

def choose_strategy
  @strategy = PagesControllerStrategy.new(self, page)
end

def page
  @page ||= Page.find params[:id]
end

pages_controller_strategy.rb:

class PagesControllerStrategy

  def initialize(controller, page)
    @controller = controller
    @page = page
  end

  def show
    # do what you what with controller and page
  end
end

However, I'd suggest you to split the behavior on the view level only:

show.html.haml:

- if page.first_type?
  = render 'pages/first_type'
- else
  // ...

EDIT:

I just found another solution, that could help you - custom constraints. http://railsdispatch.com/posts/rails-3-makes-life-better

I'm not sure if that works in your case, but I think it is worth to play with routes more.

you can do it with before_filter, but separating STI models into different controllers isn't good solution. I totally agree with next quote

This may not always apply, but I have yet to see a case where STI works well with multiple controllers. If we are using STI, our objects share a set of IDs and attributes, and therefore should all be accessed in basically the same way (find by some attribute, sort by some attribute, restrict to administrators, etc). If presentation varies greatly we may want to render different model-specific views from our controller. But if object access varies so much that it suggests separate controllers, then STI may not have been the correct design choice.

took here http://code.alexreisner.com/articles/single-table-inheritance-in-rails.html

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM