Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

i.e.

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

like image 618
potapuff Avatar asked Dec 07 '25 20:12

potapuff


1 Answers

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.

like image 99
cutalion Avatar answered Dec 09 '25 14:12

cutalion