복붙노트

[RUBY-ON-RAILS] STI는 레일에 서브 클래스에 대한 모범 사례 경로를 처리하는

RUBY-ON-RAILS

STI는 레일에 서브 클래스에 대한 모범 사례 경로를 처리하는

내 레일 뷰와 컨트롤러는 redirect_to, LINK_TO 및 form_for 메소드 호출에 산재 해있다. 때로는 LINK_TO와 redirect_to은 (예를 들어, LINK_TO '새 사람', new_person_path)를 연결하고있는 경로의 명시 적 있지만, 여러 번 경로 (예를 들어 LINK_TO '쇼', 사람을) 암시 적이다.

내 모델에 약간의 단일 테이블 상속 (STI)를 추가 (예를 들어 직원 <사람),이 모든 메소드는 서브 클래스의 인스턴스에 대한 중단 (직원을 말한다) <: 0x000001022bcd40> 0x0000010226d038 #의 때 번호 '미정 방법 employee_path 그것에 오류 @person LINK_TO 레일 실행한다. 레일 직원 인 객체의 클래스 이름에 의해 정의 된 경로를 찾고 있습니다. 이 직원 노선 정의되지 않은 및 조치 중 하나를 정의되지 않도록 어떤 직원 컨트롤러가 없습니다.

이 질문은 전에 물어되었습니다

가장 우아한 보인다 가장 공감대가 있습니다 (하지만이 모든 우아하고도 그 정도 합의 아니다) 대답 그래서, 당신의 routes.rb에 리소스를 추가합니다. 이 form_for에 대한 작업을하지 않는 경우를 제외하고. 좀 선명도를 필요로한다! 위의 선택을 증류하기 위해, 내 옵션은

이러한 모든 충돌과 답변, 나는 판결을해야합니다. 좋은 답변이없는 것처럼 날 것으로 보인다. 이것을 레일의 설계에 실패? 그렇다면, 그것은 고정받을 수 있음 버그? 하지 않을 경우 또는, 나는이 희망하고있는 사람이 똑바로 나를 설정 각 옵션의 장단점을 통해서 나를 걸어 (또는 그 옵션이 아닌 이유를 설명), 그리고 어느 쪽이 정답, 그리고 왜 할 수 있습니다. 아니면 웹에서 찾는 게 아니에요하는 정답이있다?

해결법

  1. ==============================

    1.이것은 내가 최소한의 부작용으로 가지고 올 수 있었던 가장 간단한 솔루션입니다.

    이것은 내가 최소한의 부작용으로 가지고 올 수 있었던 가장 간단한 솔루션입니다.

    class Person < Contact
      def self.model_name
        Contact.model_name
      end
    end
    

    예상대로 이제 url_for @person는 contact_path에 매핑합니다.

    그것이 작동하는 방법 : URL 헬퍼는 모델에 반영하기 위해 YourModel.model_name에 의존 (많은 것들 사이에) 단수 / 복수의 루트 키를 생성합니다. 여기 사람은 기본적으로 내가 물어 바로 연락 친구처럼 해요 말하고있다.

  2. ==============================

    2.저도 같은 문제가 있었다. STI를 사용 후, form_for 방법은 잘못된 자식 URL에 게시했다.

    저도 같은 문제가 있었다. STI를 사용 후, form_for 방법은 잘못된 자식 URL에 게시했다.

    NoMethodError (undefined method `building_url' for
    

    나는 자식 클래스에 대한 별도의 경로에 추가하고 같은 컨트롤러를 가리키는 결국

     resources :structures
     resources :buildings, :controller => 'structures'
     resources :bridges, :controller => 'structures'
    

    또한 :

    <% form_for(@structure, :as => :structure) do |f| %>
    

    이 경우 구조 건물은 실제로 (자식 클래스)

    그것은이 form_for와 함께 제출 수행 한 후 나를 위해 작동하는 것 같다.

  3. ==============================

    3."form_for"사용할 수 있도록 할 것이다이 방법을 사용하여, https://stackoverflow.com/a/605172/445908 : 나는 당신을 살펴 보시기 바랍니다.

    "form_for"사용할 수 있도록 할 것이다이 방법을 사용하여, https://stackoverflow.com/a/605172/445908 : 나는 당신을 살펴 보시기 바랍니다.

    ActiveRecord::Base#becomes
    
  4. ==============================

    4.경로의 유형을 사용합니다 :

    경로의 유형을 사용합니다 :

    resources :employee, controller: 'person', type: 'Employee' 
    

    http://samurails.com/tutorial/single-table-inheritance-with-rails-4-part-2/

  5. ==============================

    5.@Prathan Thananart의 아이디어에 따라 아무것도 파괴하지하려고합니다. (이 때문에 많은 마법 관련된 있도록)

    @Prathan Thananart의 아이디어에 따라 아무것도 파괴하지하려고합니다. (이 때문에 많은 마법 관련된 있도록)

    class Person < Contact
      model_name.class_eval do
        def route_key
         "contacts"
        end
        def singular_route_key
          superclass.model_name.singular_route_key
        end
      end
    end
    

    예상대로 이제 url_for @person는 contact_path에 매핑합니다.

  6. ==============================

    6.나도이 문제에 문제가 및 우리와 비슷한 질문에 대한이 답변으로 온되었다. 그것은 나를 위해 일했습니다.

    나도이 문제에 문제가 및 우리와 비슷한 질문에 대한이 답변으로 온되었다. 그것은 나를 위해 일했습니다.

    form_for @list.becomes(List)
    

    여기에 표시된 답변 : 같은 컨트롤러와 STI 경로를 사용

    주로 당신의 form_for처럼 STI 문제를 해결하기 위해 사용 된 .becomes 방법을 정의한다.

    여기에 정보를 .becomes : http://apidock.com/rails/ActiveRecord/Base/becomes

    슈퍼 늦게 반응은, 그러나 이것은 내가 찾을 수있는 가장 좋은 대답과 나를 위해 잘 작동. 이것은 어떤 사람을 도움이되기를 바랍니다. 건배!

  7. ==============================

    7.좋아, 필자는 아마도이 다른 사람을 도움이 될 것입니다, 레일의이 지역에서 좌절의 톤을 가지고, 다음과 같은 접근 방식에 도착했습니다.

    좋아, 필자는 아마도이 다른 사람을 도움이 될 것입니다, 레일의이 지역에서 좌절의 톤을 가지고, 다음과 같은 접근 방식에 도착했습니다.

    우선 위의 그물의 주위에 다수의 솔루션은 클라이언트가 제공 매개 변수에 대한 constantize를 사용하는 것이 좋습니다 것을 유의하십시오. 루비 따라서 공격자가 임의의 문자를 생성하고 사용 가능한 메모리를 소비 할 수없는 쓰레기 수집 문자처럼 이것은 알려진 DoS 공격 벡터이다.

    나는 모델 서브 클래스의 인스턴스를 지원하며, 위의 contantize 문제로부터 안전하다 아래의 방법을 구현했습니다. 그것은 4 않습니다 레일 것과 매우 유사하지만 (레일 4와 달리) 서브 클래스와 레일 3 작품 중 하나 개 이상의 수준을 수 있습니다.

    # initializers/acts_as_castable.rb
    module ActsAsCastable
      extend ActiveSupport::Concern
    
      module ClassMethods
    
        def new_with_cast(*args, &block)
          if (attrs = args.first).is_a?(Hash)
            if klass = descendant_class_from_attrs(attrs)
              return klass.new(*args, &block)
            end
          end
          new_without_cast(*args, &block)
        end
    
        def descendant_class_from_attrs(attrs)
          subclass_name = attrs.with_indifferent_access[inheritance_column]
          return nil if subclass_name.blank? || subclass_name == self.name
          unless subclass = descendants.detect { |sub| sub.name == subclass_name }
            raise ActiveRecord::SubclassNotFound.new("Invalid single-table inheritance type: #{subclass_name} is not a subclass of #{name}")
          end
          subclass
        end
    
        def acts_as_castable
          class << self
            alias_method_chain :new, :cast
          end
        end
      end
    end
    
    ActiveRecord::Base.send(:include, ActsAsCastable)
    

    무슨 유사 많은 위에 제시 한 '으로 개발되어 문제의 sublclass로드'에 대한 다양한 접근을 시도 후, 나는 확실하게 일을 내 모델 클래스에 'require_dependency'를 사용했다 유일한을 발견했다. 이 것을 보장 클래스 로딩 개발에서 제대로 작동하고 생산에 아무런 문제가 발생하지 않습니다. 개발없이 'require_dependency'AR은 SQL이 유형 열에 일치에 방출 영향 모든 서브 클래스에 대해 알고 실 거예요. 'require_dependency'없이 또한 당신은 또한 같은 시간에 모델 클래스의 여러 버전이있는 상황에서 끝낼 수 있습니다! (예. 당신이 기본 또는 중간 클래스를 변경할 때 이런 일이 수, 하위 클래스는 항상 다시로드하는 것하고 기존 클래스의 왼쪽 서브 클래스입니다하지 않습니다)

    # contact.rb
    class Contact < ActiveRecord::Base
      acts_as_castable
    end
    
    require_dependency 'person'
    require_dependency 'organisation'
    

    위의 제안 또한 MODEL_NAME를 오버라이드 (override)하지 않는 내가 국제화를 사용하고 다른 서브 클래스의 속성, 예를 들어 다른 문자열을 필요로하기 때문에 : tax_identifier는 조직에 대한 'ABN'와 (호주에서) 사람에 대한 'TFN'을하게합니다.

    위의 제안 또한 유형을 설정, 경로 매핑을 사용 :

    resources :person, :controller => 'contacts', :defaults => { 'contact' => { 'type' => Person.sti_name } }
    resources :organisation, :controller => 'contacts', :defaults => { 'contact' => { 'type' => Organisation.sti_name } }
    

    경로 매핑뿐만 아니라, 나는 InheritedResources 및 SimpleForm을 사용하고 있는데 나는 새로운 행동에 대한 다음과 같은 일반적인 형태의 래퍼를 사용합니다 :

    simple_form_for resource, as: resource_request_name, url: collection_url,
          html: { class: controller_name, multipart: true }
    

    ... 편집 작업을 위해 :

    simple_form_for resource, as: resource_request_name, url: resource_url,
          html: { class: controller_name, multipart: true }
    

    그리고이 작품을 만들기 위해, 내베이스 ResourceContoller 나는 뷰의 도우미 방법으로 InheritedResource의 resource_request_name 노출 :

    helper_method :resource_request_name 
    

    당신이 InheritedResources을 사용하지 않는 경우, 다음 'ResourceController'에 다음과 같은 것을 사용 :

    # controllers/resource_controller.rb
    class ResourceController < ApplicationController
    
    protected
      helper_method :resource
      helper_method :resource_url
      helper_method :collection_url
      helper_method :resource_request_name
    
      def resource
        @model
      end
    
      def resource_url
        polymorphic_path(@model)
      end
    
      def collection_url
        polymorphic_path(Model)
      end
    
      def resource_request_name
        ActiveModel::Naming.param_key(Model)
      end
    end
    

    다른 사람의 경험과 개선을 듣고 항상 행복.

  8. ==============================

    8.나는 최근에 레일 3.0 응용 프로그램에서 안정적인 STI 패턴 작업을 얻을 나의 시도를 문서화. 여기서 TL이다; DR 버전 :

    나는 최근에 레일 3.0 응용 프로그램에서 안정적인 STI 패턴 작업을 얻을 나의 시도를 문서화. 여기서 TL이다; DR 버전 :

    # app/controllers/kase_controller.rb
    class KasesController < ApplicationController
    
      def new
        setup_sti_model
        # ...
      end
    
      def create
        setup_sti_model
        # ...
      end
    
    private
    
      def setup_sti_model
        # This lets us set the "type" attribute from forms and querystrings
        model = nil
        if !params[:kase].blank? and !params[:kase][:type].blank?
          model = params[:kase].delete(:type).constantize.to_s
        end
        @kase = Kase.new(params[:kase])
        @kase.type = model
      end
    end
    
    # app/models/kase.rb
    class Kase < ActiveRecord::Base
      # This solves the `undefined method alpha_kase_path` errors
      def self.inherited(child)
        child.instance_eval do
          def model_name
            Kase.model_name
          end
        end
        super
      end  
    end
    
    # app/models/alpha_kase.rb
    # Splitting out the subclasses into separate files solves
    # the `uninitialize constant AlphaKase` errors
    class AlphaKase < Kase; end
    
    # app/models/beta_kase.rb
    class BetaKase < Kase; end
    
    # config/initializers/preload_sti_models.rb
    if Rails.env.development?
      # This ensures that `Kase.subclasses` is populated correctly
      %w[kase alpha_kase beta_kase].each do |c|
        require_dependency File.join("app","models","#{c}.rb")
      end
    end
    

    이 방법을 사용하면 다른 사람이 STI와 접근 방법 한 것을 다른 문제의 숫자뿐만 아니라 나열하는 문제를 중심으로 가져옵니다.

  9. ==============================

    9.당신이 더 중첩 된 경로가있는 경우,이 시도 할 수 있습니다 :

    당신이 더 중첩 된 경로가있는 경우,이 시도 할 수 있습니다 :

    resources :employee, path: :person, controller: :person
    

    또는 당신은 다른 방법을 가서 여기에 설명 된 것처럼 일부 OOP-마법을 사용할 수 있습니다 https://coderwall.com/p/yijmuq

    두 번째 방법에서는 모든 중첩 모델과 유사한 헬퍼를 만들 수 있습니다.

  10. ==============================

    10.여기가 형태로 우리가 사용하는 응용 프로그램 전반에 걸쳐 작동해야하는 안전하고 깨끗 방법입니다.

    여기가 형태로 우리가 사용하는 응용 프로그램 전반에 걸쳐 작동해야하는 안전하고 깨끗 방법입니다.

    resources :districts
    resources :district_counties, controller: 'districts', type: 'County'
    resources :district_cities, controller: 'districts', type: 'City'
    

    그럼 난 내 양식에 있습니다. : 지역 : 이것에 대한 추가 부분은 그대로.

    = form_for(@district, as: :district, html: { class: "form-horizontal",         role: "form" }) do |f|
    

    도움이 되었기를 바랍니다.

  11. ==============================

    11.나는이 같은 STI 상속을 고려하는 경우 :

    나는이 같은 STI 상속을 고려하는 경우 :

    class AModel < ActiveRecord::Base ; end
    class BModel < AModel ; end
    class CModel < AModel ; end
    class DModel < AModel ; end
    class EModel < AModel ; end
    

    '응용 프로그램 / 모델 / a_model.rb'에 내가 추가 :

    module ManagedAtAModelLevel
      def model_name
        AModel.model_name
      end
    end
    

    그리고 다음 AModel 클래스 :

    class AModel < ActiveRecord::Base
      def self.instanciate_STI
        managed_deps = { 
          :b_model => true,
          :c_model => true,
          :d_model => true,
          :e_model => true
        }
        managed_deps.each do |dep, managed|
          require_dependency dep.to_s
          klass = dep.to_s.camelize.constantize
          # Inject behavior to be managed at AModel level for classes I chose
          klass.send(:extend, ManagedAtAModelLevel) if managed
        end
      end
    
      instanciate_STI
    end
    

    따라서 난 쉽게 내가 기본 하나를 사용하고자하는 모델을 선택하고,이 경우에도 하위 클래스 정의를 건드리지 않고 있습니다. 매우 건조.

  12. ==============================

    12.이 방법은 나를 위해 확인 작업 (기본 클래스에서이 메소드를 정의) :

    이 방법은 나를 위해 확인 작업 (기본 클래스에서이 메소드를 정의) :

    def self.inherited(child)
      child.instance_eval do
        alias :original_model_name :model_name
        def model_name
          Task::Base.model_name
        end
      end
      super
    end
    
  13. ==============================

    13.당신은 반환 목적을 그라우팅에 대한 부모 개체를 더미 있다는 방법을 만들 수 있습니다

    당신은 반환 목적을 그라우팅에 대한 부모 개체를 더미 있다는 방법을 만들 수 있습니다

    class Person < ActiveRecord::Base      
      def routing_object
        Person.new(id: id)
      end
    end
    

    다음 단순히 form_for @ employee.routing_object 전화 유형이없는 이는 Person 클래스의 객체를 반환합니다

  14. ==============================

    14.하여 상위 모델에 다음 prathan-thananart 대답 @ 다음, 그리고 여러 STI 클래스에 대한, 당신은 추가 할 수 있습니다 ->

    하여 상위 모델에 다음 prathan-thananart 대답 @ 다음, 그리고 여러 STI 클래스에 대한, 당신은 추가 할 수 있습니다 ->

    class Contact < ActiveRecord::Base
      def self.model_name
        ActiveModel::Name.new(self, nil, 'Contact')
      end
    end
    

    [: 접촉] 대신 PARAMS [: contact_person, PARAMS [: contact_whatever 즉 연락처 데이터와 각 양식은 PARAMS으로 PARAMS를 보내도록 할 것입니다.

  15. ==============================

    15.hackish 솔루션의 목록에 있지만, 또 다른 하나.

    hackish 솔루션의 목록에 있지만, 또 다른 하나.

    class Parent < ActiveRecord::Base; end
    
    Class Child < Parent
      def class
        Parent
      end
    end
    

    레일 2.x 및 3.x에 작품

  16. from https://stackoverflow.com/questions/4507149/best-practices-to-handle-routes-for-sti-subclasses-in-rails by cc-by-sa and MIT license