Думаю каждый кто работал с под-доменами в Rails 3 видел данный скринкаст.
Когда столкнулся с этим примером стояла задача сделать динамические поддомены + некоторые фиксированные. Динамические должны были соответствовать некоторому полю одной из моделей. Таким образом были выдвинуты условия для конечного решения:
Для реализации первого пункта за основу был взят выше указанный пример и расширен возможностью передачи своего имени поддомена или их массива.
subdomain/base.rb
Думаю объяснять тут ничего не надо.
Далее дошла очередь и для реализации второго пункта.
В данном классе в методе проверки совпадения происходит получения экземпляра класса по его имени. Далее как проверяем наличие поддомена в таблице.
subdomain/active_record.rb
И третьим этапом было упрощение записи правил в routes.rb. Если использовать тот вариант что предлагает пример, то получались довольно громоздкие записи и не совсем понятные на первый взгляд.
Чтобы добавить свой метод необходимо расширить класс, отвечающий за пути, а именно ActionDispatch::Routing::Mapper
Для этого был написан модуль, который примешивался к основному классу в процессе загрузки приложения.
config/initializers/subdomain.rb
subdomain/extension.rb
После чего указанные ранее конструкции были заменены на следующие:
В итоге получился довольно удобный интерфейс для работы с поддоменами.
Конструктивная критика и советы приветствуются. Заранее приношу извинения за местами кривой код.
PS: файлы доступны в архиве.
PPS: касательно подсветки синтаксиса, в предпросмотре все есть, в конечном варианте почему-то нет.
Когда столкнулся с этим примером стояла задача сделать динамические поддомены + некоторые фиксированные. Динамические должны были соответствовать некоторому полю одной из моделей. Таким образом были выдвинуты условия для конечного решения:
- возможность указания фиксированного поддомена, либо группы поддоменов;
- возможность привязки к полю из модели ActiveRecord;
- удобный синтаксис для записи всего этого в routes.rb.
Реализация
Для реализации первого пункта за основу был взят выше указанный пример и расширен возможностью передачи своего имени поддомена или их массива.
subdomain/base.rb
module Subdomain class Base attr_accessor :subdomain def initialize(param = ["www"] ) @subdomains = case param.class.to_s when "String", "Symbol" [param] when "Array" param else [] end end def matches?(request) self.subdomain?( request ) && ( @subdomains.map {|i| i.to_s == request.subdomain }.include? true ) end protected def subdomain?(request) request.subdomain.present? and ( request.subdomain != "www" ) end end end
Думаю объяснять тут ничего не надо.
Далее дошла очередь и для реализации второго пункта.
В данном классе в методе проверки совпадения происходит получения экземпляра класса по его имени. Далее как проверяем наличие поддомена в таблице.
subdomain/active_record.rb
class Subdomain::ActiveRecord < Subdomain::Base attr_reader :model attr_accessor :field def initialize(params) p = params.first @model = p[1] @field = p[0] end def matches?(request) obj = case @model.class when String @model.classify.constantize when Symbol @model.to_s.classify.constantize else @model end subdomain?(request) and ( obj.superclass == ActiveRecord::Base ) and obj.where(field.to_sym => request.subdomain).first.present? end end
И третьим этапом было упрощение записи правил в routes.rb. Если использовать тот вариант что предлагает пример, то получались довольно громоздкие записи и не совсем понятные на первый взгляд.
constraints( Subdomain::ActiveRecord.new Site::User, :login ) do root :to => "main#index" end constraints( Subdomain::Base.new [:admin] ) do scope :module => "admin", :as => :admin do root :to => "main#index" end end
Чтобы добавить свой метод необходимо расширить класс, отвечающий за пути, а именно ActionDispatch::Routing::Mapper
Для этого был написан модуль, который примешивался к основному классу в процессе загрузки приложения.
config/initializers/subdomain.rb
ActionDispatch::Routing::Mapper.send(:include, Subdomain::Extension)
subdomain/extension.rb
module Subdomain module Extension def subdomain( sub, params = {} ) if params[:scope] scope :module => params[:scope].to_s, :as => params[:scope].to_sym do unscoped_subdomain( sub ) { yield } end else m = case sub.class.to_s when "Symbol", "String" sub.to_s when "Array" sub.first when "Hash" nil end if m.blank? or params.key?(:scope) unscoped_subdomain( sub ) { yield } else scope :module => m, :as => m.to_sym do unscoped_subdomain( sub ) { yield } end end end end def unscoped_subdomain( sub ) case sub.class.to_s when "Symbol", "String", "Array" constraints( Subdomain::Base.new sub ) { yield } when "Hash" p = sub.first inst = "subdomain/#{p[0].to_s}".classify.constantize constraints( inst.new p[1] ) { yield } end end end end
После чего указанные ранее конструкции были заменены на следующие:
subdomain( :active_record => { :login => Site::user } ) do root :to => "main#index" end subdomain( :admin ) do root :to => "main#index" end #равносильно subdomain( :base => :admin ) do root :to => "main#index" end
Результат
В итоге получился довольно удобный интерфейс для работы с поддоменами.
subdomain( "subdomain" ) do root :to => "main#index"# controller => subdomain/main path => subdomain_root end subdomain( "subdomain", :scope => "probe" ) do root :to => "main#index"# controller => probe/main path => probe_root end subdomain( "subdomain", :scope => false ) do root :to => "main#index"# controller => main end subdomain( ["subdomain1", "subdomain2"], :scope => "test" ) do root :to => "main#index" # controller => test/main end subdomain( ["subdomain1", "subdomain2"] , :scope => false ) do root :to => "main#index" # controller => main end subdomain(:имя_класса => параметры ) do root :to => "main#index"# controller => main end #для ActiveRecord subdomain( :active_record => { :имя_поля => модель } ) do root :to => "main#index"# controller => main end subdomain( :active_record => { :имя_поля => модель }, :scope => "models" ) do root :to => "main#index"# controller => models/main end
Конструктивная критика и советы приветствуются. Заранее приношу извинения за местами кривой код.
PS: файлы доступны в архиве.
PPS: касательно подсветки синтаксиса, в предпросмотре все есть, в конечном варианте почему-то нет.